2

Suppose we have Sub inherit compareTo from Super:

class Super implements Comparable<Super> {
    @Override public int compareTo(Super o) {
        return 0;
    }
}

class Sub extends Super {}

And generic method max that takes a list of Comparable:

public static <T extends Comparable<T>> T max(List<T> list) {
    return null;
}

If we try to call max with the list of Sub's we will get a compile-time error because Sub doesn't implement Comparable<Sub>. This is what I expected!

max(new ArrayList<Sub>()); // error, Sub should implement Comparable<Sub>

Then I change max arguments to accept just two instances of T and test it:

public static <T extends Comparable<T>> T max(T t, T t2) {
    return null;
} 
max(new Sub(), new Sub());

No compile-time error, non runtime!

What is the difference between max methods in terms of type safety?

  • 2
    It's because a `Sub` is a `Super`, but a `Comparable` is not a `Comparable` and a `List` is not a `List`. http://stackoverflow.com/q/2745265/3973077 – Paul Boddington Apr 14 '16 at 22:12

1 Answers1

3

max(T,T) lets the compiler infer that T can be Super and do a widening conversion on the arguments.

On the other hand, max(List<T>) is not so flexible, because generics are invariant (explained in Paul's link). The compiler can't perform a conversion on the List, so T has to be exactly Sub.

In both cases, you should change the type variable bound to this:

public static <T extends Comparable<? super T>> T max(List<T> list) {...}
//                                  ^^^^^^^^^

It's less restrictive and will let you do e.g.:

Sub s0 = max(new ArrayList<Sub>());
Sub s1 = max(new Sub(), new Sub());

Line 2 also wouldn't compile with the more restrictive bound, which you may have missed because you ignore the return value in your example.

(The ? super T is also how Java SE does it.)

Radiodef
  • 37,180
  • 14
  • 90
  • 125