24

Why does Collections.sort(List<T>) have the signature :

public static <T extends Comparable<? super T>> void sort(List<T> list) 

and not :

public static <T extends Comparable<T>> void sort(List<? extends T> list)
  • I understand that they both would serve the same purpose; so why did the framework developers use the first option?
  • Or are these declarations really different?
Raedwald
  • 46,613
  • 43
  • 151
  • 237
prvn
  • 684
  • 6
  • 21

3 Answers3

19

Your proposed signature would probably work in Java-8. However in previous Java versions type inference was not so smart. Consider that you have List<java.sql.Date>. Note that java.sql.Date extends java.util.Date which implements Comparable<java.util.Date>. When you compile

List<java.sql.Date> list = new ArrayList<>();
Collections.sort(list);

It perfectly works in Java-7. Here T is inferred to be java.sql.Date which is actually Comparable<java.util.Date> which is Comparable<? super java.sql.Date>. However let's try your signature:

public static <T extends Comparable<T>> void sort(List<? extends T> list) {}

List<java.sql.Date> list = new ArrayList<>();
sort(list);

Here T should be inferred as java.util.Date. However Java 7 specification does not allow such inference. Hence this code can be compiled with Java-8, but fails when compiled under Java-7:

Main.java:14: error: method sort in class Main cannot be applied to given types;
        sort(list);
        ^
  required: List<? extends T>
  found: List<Date>
  reason: inferred type does not conform to declared bound(s)
    inferred: Date
    bound(s): Comparable<Date>
  where T is a type-variable:
    T extends Comparable<T> declared in method <T>sort(List<? extends T>)
1 error

Type inference was greatly improved in Java-8. Separate JLS chapter 18 is dedicated to it now, while in Java-7 the rules were much simpler.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 1
    But how does static > T max(Collection extends T> c) works fine ...is this again a inference issue? – prvn Mar 01 '16 at 06:13
  • @prvn, here you have `Comparable super T>` instead of `Comparable` like in your proposed signature. `> void sort(List extends T> list)` would also work, but adding `? extends T` is completely unnecessary here, while reasonable in `max` method (as `max` returns the value). – Tagir Valeev Mar 01 '16 at 06:14
  • 8
    There is another point. When you have a wildcard type like `List extends T> list` you can’t do `list.set(index, list.get(anotherIndex))` inside the method, due to how wildcard types work. This can be circumvented with an internal helper method capturing `? extends T` into another type variable (or by using unchecked operations as internal implementations often do), but still, a type without wildcards is cleaner for a list that is going to be modified. – Holger Mar 01 '16 at 11:04
  • @Holger: But that's an internal implementation concern for the implementor. We are just looking at the public API, which should not be affected by internal implementation details. – newacct Mar 02 '16 at 22:18
  • 2
    @newacct: if two variants make no difference to the caller, you’ll likely choose the one that allows an easier implementation. Nevertheless, developers got used to initially think that a parameter of a `List extends …>` type is not going to be modified due to the *PECS* rule and need a second look at the method if you use that for a `sort` method. It’s more than an implementation concern. – Holger Mar 03 '16 at 07:54
9
// 0
public static <T extends Comparable<? super T>> void sort0(List<T> list) 

// 1
public static <T extends Comparable<T>> void sort1(List<? extends T> list)

These signatures differ because they impose different requirements on the relationship between type T and the type argument to Comparable in the definition of T.

Suppose for example that you have this class:

class A implements Comparable<Object> { ... }

Then if you have

List<A> list = ... ;
sort0(list); // works
sort1(list); // fails

The reason sort1 fails is that there is no type T that is both comparable to itself and that is, or is a supertype of, the list's type.

It turns out that class A is malformed, because objects that are Comparable need to meet certain requirements. In particular reversing the comparison should reverse the sign of the result. We can compare an instance of A to an Object but not vice-versa, so this requirement is violated. But note that this is a requirement of the semantics of Comparable and is not imposed by the type system. Considering only the type system, the two sort declarations really are different.

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
4

They differ because ? super T is less restrictive than T. It is a Lower Bounded Wildcard (the linked Java Tutorial says, in part)

The term List<Integer> is more restrictive than List<? super Integer> because the former matches a list of type Integer only, whereas the latter matches a list of any type that is a supertype of Integer.

Replace Integer with T and it means a T or a java.lang.Object.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • but i am increasing flexibilty in the argument of sort() ..using ? extends, – prvn Mar 01 '16 at 05:30
  • 1
    Yes, nobody said that `List super Integer>` is the same as `List`. The second version uses `extends` and the question is if this makes it equal. – JojOatXGME Mar 01 '16 at 05:30
  • 1
    I don't think your answer is directly related the question. – yuxh Feb 12 '18 at 05:01