2

I'm working my way through the Oracle java tutorials. I'm currently on generics and I'm confused by question 8 at the end of the lesson, the solution to which is provided below. Specifically, I don't understand the rationale behind having T extend both Object and Comparable<? super T>. Isn't it enough for T to simply extend Comparable<? super T>, whats the advantage of having T extend Object also?

Question:

Write a generic method to find the maximal element in the range [begin, end) of a list.

Answer:

import java.util.*;

public final class Algorithm {
    public static <T extends Object & Comparable<? super T>>
        T max(List<? extends T> list, int begin, int end) {

        T maxElem = list.get(begin);

        for (++begin; begin < end; ++begin)
            if (maxElem.compareTo(list.get(begin)) < 0)
                maxElem = list.get(begin);
        return maxElem;
    }
}
Duncan3142
  • 371
  • 2
  • 12
  • The question that this post is marked a duplicate of is certainly related, but it's not quite a duplicate. The other question is why `Collections.max` has `extends Object`, but in the linked tutorial question/answer from this question, the answer (backwards compatibility) doesn't apply. – rgettman Oct 12 '15 at 21:09
  • @rgettman Agreed. I originally linked to the question that this one is now a "duplicate" of **and** to http://stackoverflow.com/questions/18411527/how-to-write-a-generic-method-to-find-the-maximal-element-and-invoke-that-method , but removed the first link for exactly this reason: Backward compatibility does not apply here. The answer to the other question explains the type erasure thing pretty well, so closing may still be justified. – Marco13 Oct 12 '15 at 21:39

1 Answers1

1

The only reason I can find to have <T extends Object & Comparable<? super T>, as opposed to T extends Comparable<? super T>, is for backwards compatibility when generifying a method.

According to Angelika Langer's Java Generics FAQ:

Occasionally, one must pay attention to the fact that a generification might change the signature of some methods in the byte code. Changing the signature will break existing code that cannot be recompiled and relies on the binary compatibility of the old and new version of the .class file.

In this case, Java's own Collections.max used to return Object before 1.5, the arrival of generics. When generified, this method could be declared without extends Object, and it would work correctly in isolation.

public static <T extends Comparable<? super T>> T max(Collection <? extends T> coll)

However, the erasure of this method, for bytecode purposes, has this method returning Comparable, instead of Object, which is a backwards incompatibility.

To resolve this, extends Object was inserted as the first bound, so that the erasure of the return type of this method would remain Object.

This resolved the backwards incompatibility issue for Java generics in 1.5.

However, the question in the tutorial stated:

Write a generic method to find the maximal element in the range [begin, end) of a list.

You are writing your own new method, so there is no backwards compatibility to maintain yet. The extends Object in the answer to this tutorial question is unnecessary.

In addition

In the bytecode (javap -c Algorithm.class), all types undergo type erasure, even the local variable maxElem.

  public static <T extends java.lang.Comparable<? super T>> T max(java.util.List<? extends T>, int, int);
Code:
   0: aload_0
   1: iload_1
   2: invokeinterface #2,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;

The get method has returned an Object. Then how does it invoke compareTo in Comparable?

   7: astore_3
   8: iinc          1, 1
  11: iload_1
  12: iload_2
  13: if_icmpge     49
  16: aload_3
  17: checkcast     #3                  // class java/lang/Comparable
  20: aload_0
  21: iload_1
  22: invokeinterface #2,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  27: invokeinterface #4,  2            // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I

The compiler has inserted an implicit cast to Comparable so that the compareTo method can be called. It has asserted that the objects in the list are Comparable because of the second upper bound, Comparable<? super T>.

  32: ifge          43
  35: aload_0
  36: iload_1
  37: invokeinterface #2,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
  42: astore_3
  43: iinc          1, 1
  46: goto          11
  49: aload_3
  50: areturn
}
rgettman
  • 176,041
  • 30
  • 275
  • 357