prvn
prvn

Reputation: 694

Collections.sort() declaration: why <? super T> rather than <T>

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)

Upvotes: 25

Views: 1533

Answers (3)

Stuart Marks
Stuart Marks

Reputation: 132460

// 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.

Upvotes: 9

Tagir Valeev
Tagir Valeev

Reputation: 100269

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.

Upvotes: 19

Elliott Frisch
Elliott Frisch

Reputation: 201467

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.

Upvotes: 4

Related Questions