Java Generics – Wildcard vs Type Parameter in Method Signatures

genericsjava

I'm having a hard time trying to figure out why the two methods declarations have different results.

On the first method signature its obvious you can't compare Apples with Apples and Oranges with Orange, because none of them implement Comparable<>.

But on the second method I was surprised that you could actually compare Apples with Apples and Oranges with Oranges.

I remember on a previous version of IntelliJ (14) a call to the second method with a List of Apples or Oranges was giving a compilation error. But the newer versions of IntelliJ don't report that problem anymore.

I would appreciate if someone could explain me what's the difference between the two methods signatures using the Fruit example.

class Fruit implements Comparable<Fruit> {}

class Apple extends Fruit {
}

class Orange extends Fruit {
}

static <T extends Comparable<T>> T maxSimple(Collection<T> coll) {
}

static <T extends Comparable<T>> T max(Collection<? extends T> coll) {
}

Best Answer

What are the constraints on T?

In your first example, T must equal the collection type and be comparable to exactly itself. Note that List<Apple> does not extend List<Fruit>, and if it did, you could add Oranges to the apple list.

Try T as Apple : Apple is not Comparable<Apple>, so this fails. Try T as Fruit : Collection<Fruit> is not a supertype of Collection<Apple>, so this fails

In your second example, you allow the collection type to range over the subtypes of T, so T can be Fruit and you have Collection< any fruit subtype >

There is a third option that you havent considered:

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

Here we allow T to be bound to Apple, as Apple is comparable to one of its superclasses