Why is SomeClass<? super T> not equivalent to SomeClass<T> in Java generic types?

I noticed the specificaition for Collections.sort:

public static <T> void sort(List<T> list, Comparator<? super T> c)

Why is the "? super" necessary here? If ClassB extends ClassA, then wouldn't we have a guarantee that a Comparator<ClassA> would be able to compare two ClassB objects anyway, without the "? super" part?

In other words, given this code:

List<ClassB> list = . . . ;
Comparator<ClassA> comp = . . . ;
Collections.sort(list, comp);

why isn't the compiler smart enough to know that this is OK even without specifying "? super" for the declaration of Collections.sort()?

Asked by: Joyce870 | Posted: 21-01-2022

Answer 1

Josh Bloch had a talk at Google I/O this year, called Effective Java Reloaded, which you may find interesting. It talks about a mnemonic called "Pecs" (producer extends, consumer super), which explains why you use ? extends T and ? super T in your input parameters (only; never for return types), and when to use which.

Answered by: Max900 | Posted: 22-02-2022

Answer 2

There's a really nice (but twisty) explanation of this in More Fun with Wildcards.

Answered by: Julian852 | Posted: 22-02-2022

Answer 3

This is similar to C#, I just learned about it a couple days ago as to why (the hard way, and then the PDC informative way).

Assume Dog extends Animal

Blah<Dog> is not the same as Blah<Animal> they have completely different type signatures even though Dog extends Animal.

For example assume a method on Blah<T>:

T Clone();  

In Blah<Dog> this is Dog Clone(); while in Blah<Animal> this is Animal Clone();.

You need a way to distinguish that the compiler can say that Blah<Dog> has the same public interface of Blah<Animal> and that's what <? super T> indicates - any class used as T can be reduced to its super class in terms of Blah<? super T>.

(In C# 4.0 this would be Blah<out T> I believe.)

Answered by: Kimberly516 | Posted: 22-02-2022

Answer 4

It's obvious to you that, in the case of Comparator, any ancestor of T would work. But the compiler doesn't know that class Comparator functions like that - it just needs to be told whether it should allow <T> or <? super T>.

Viewed another way, yes it's true that any Comparator of an ancestor would work in this case - and the way the library developer says that is to use <? super T>.

Answered by: Brooke764 | Posted: 22-02-2022

Answer 5

The simple answer to your question is that the library designer wanted to give the maximum flexibility to the user of the library; this method signature for example allows you to do something like this:

List<Integer> ints = Arrays.asList(1,2,3);
Comparator<Number> numberComparator = ...;

Collections.sort(ints, numberComparator);

Using the wildcard prevents you from being forced to use a Comparator<Integer>; the fact that the language requires a wildcard to be specified by the library designer enables him or her to either permit or restrict such use.

Answered by: Miranda714 | Posted: 22-02-2022

Similar questions

generics - Java: How to set a default for "T" in SomeClass<T>?

Is there a way to specify a default type for a generic template? Let's say I have a Monkey class. Monkeys can live in different Environments, such as Jungle or Zoo: public class Monkey&lt;T extends Environment&gt; { T home; ... public T getHome() { return home; } } Is there a ...

Still can't find your answer? Check out these amazing Java communities for help...

Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)