Reputation: 552
Lets assume that we have
class A implements Comparable<A>
And
class B extends A
Now, I would like to have generic method that finds biggest object from collection, using compareTo
method.
If I declare this method like so:
public static <T extends Comparable<T>> T max(List<T> list)
It won't work for argument List<B> list
because B
does not parameterize Comparable
with type B
. For that method to work, it seems natural for me to change
Comparable<T>
To
Comparable<? super T>
So method after that change will look like this:
public static <T extends Comparable<? super T>> T max(List<T> list)
Now passing argument List<B> list
works fine and gives no errors. Fine. But when i change method signature to
public static <T extends Comparable<T>> T max(List<? extends T> list)
It also works fine for argument List<B> list
! And my question is WHY. Could someone explain that to me? Thanks :)
Upvotes: 2
Views: 305
Reputation: 16504
It's not the same thing.
In the first example, you're saying, "For some type T
, such that I can cast any T
to a Comparable
of a supertype of T
, then I'll accept a list of T
and return an instance of T
."
When you call the method with a List<B>
, since every B
is a Comparable<A>
, it's therefore true to say: "I can cast any B
to a Comparable
of a supertype of B
", and the type parameter T
resolves to concrete type B
.
In the second example, you're saying, "For some type T
, such that I can cast any T
to a Comparable of T
, then I'll accept any list of T
or any list of any subtype of T
, and return an instance of T
."
When you call this one with A List<B>
, then the type parameter T
resolves to A
; of course any A
is a Comparable<A>
, and you can pass a List<B>
into the method because a list of B
is indeed a list of a subtype of A
.
So that's why both will compile.
But you're asking about the purpose of being able to say List<? extends T>
. This lets you remain open to receiving collections (or other parameterized types) of subclasses of the class you're working with. This is usually what you want when you're going to receive objects of a certain type and do something with them, and usually, you'd be equally happy to do that to a subtype (since a subtype should respect the same contract).
The super
keyword means that you can work with objects whose type parameter is a superclass of your type parameter. The typical case is when you want to add an object to a collection. Suppose that T
here resolves to concrete type B
. If you received a List<A> myList
then List.add(B)
would be legal, because it's OK to add a B
to a List<A>
. It's also useful if your method returns, say, a Set<T>
- in that case, the calling code can't assign it to a Set<A>
even though everything in the set is an A
, but if you instead return Set<? super T>
then it can. In your case, you're going to call Comparable<A>.compare(B)
and here, similarly, it's legal to pass an instance of B
to Comparable<A>.compare(A)
.
There's a mnemonic for this, PECS "Producer Extends, Consumer Super" discussed here: What is PECS (Producer Extends Consumer Super)?
Upvotes: 0
Reputation: 2490
List<? extends T>
means "a List. We don't know a List of what, but whatever it is, we do know that it extends T or is T itself."
One effect of this is that when we take an element from the List, the compiler will know it as of type T. The actual runtime class of the item may be a subtype of T, as is always the case with polymorphism, but no one cares about that. We know it can be stored in a variable type T.
Another effect is that we can't put any element in the List, because we don't know of what the List is. So whatever we'd try to put inside, we don't know if it is of acceptable type, therefore we can't. But when finding the max it doesn't matter: we won't be adding anything anyway.
Therefore, a List<B>
can also be taken as a List<? extends A>
if needed (suddenly decide that we don't know don't care of what the List is, but whatever it is it extends A.) This is helping because then it allows to match the method signature, taking T as A.
Upvotes: 1