74

Just went through the implementation of Java 7's java.util.Collections class, and saw something that I don't understand. In the max function signature, why is T bounded by Object?

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        if (next.compareTo(candidate) > 0)
            candidate = next;
    }
    return candidate;
} 

max seems to work fine if the Object bound is omitted.

public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll) {
    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        if (next.compareTo(candidate) > 0)
            candidate = next;
    }
    return candidate;
}

Are there actually any situations where the bound makes a difference? If yes, please provide a concrete example.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Maxim Kirilov
  • 2,639
  • 24
  • 49
  • 4
    Duplicate of [ vs ](http://stackoverflow.com/questions/10339338/t-extends-object-e-vs-t-extends-e) although this post has both a better question and answer. – Paul Bellora Oct 21 '13 at 17:53

1 Answers1

89

The two have the same bounds but there is a subtle difference.

 <T extends Object & Comparable<? super T>> 

This will cause T to become an Object under erasure.

 <T extends Comparable<? super T>>

This will cause T to become Comparable under erasure.


In this case it is done because .max predates Java 5. We can see in this link Joachim kindly provided that the signature of .max in Java 1.4.2 is:

public static Object max(Collection coll)

Had we used <T extends Comparable<? super T>> as a bound, our signature would be

public static Comparable max(Collection coll)

Which would break the APIs. I've managed to find this page that discusses converting old APIs to generic ones and it gives .max as a specific example.

Here they explain why max is defined this way:

You also need to ensure that the revised API retains binary compatibility with old clients. This implies that the erasure of the API must be the same as the original, ungenerified API. In most cases, this falls out naturally, but there are some subtle cases. We'll examine one of the subtlest cases we've encountered, the method Collections.max(). As we saw in section More Fun with Wildcards, a plausible signature for max() is:

public static <T extends Comparable<? super T>> T max(Collection<T> coll) This is fine, except that the erasure of this signature is: public static Comparable max(Collection coll) which is different than the original signature of max(): public static Object max(Collection coll)

One could certainly have specified this signature for max(), but it was not done, and all the old binary class files that call Collections.max() depend on a signature that returns Object.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Is there a reason this would be desirable? – templatetypedef Oct 21 '13 at 07:25
  • Maybe because [`Collection.max` predates Java 5](http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#max(java.util.Collection)) and as such predates Generics. And since the return value is `T` that would change the signature of the method after erasure. – Joachim Sauer Oct 21 '13 at 07:30
  • @templatetypedef Yes, mainly in old code. Since Java didn't always have Generics having the signature of the method after erasure remain the same as the signature before generifying it is desirable. I remember reading about it in docs.oracle.com - it'll take me a bit more to find it. – Benjamin Gruenbaum Oct 21 '13 at 07:31
  • I never thought about generics signature after type erasure. – Shashank Oct 30 '17 at 14:14