As per a literature I read,we have juicy fruits implementign the following interface:
public interface Juicy<T> {
Juice<T> squeeze();
}
Using bounded type variables, following method would taks a bunch of fruits and squeeze them all:
<T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits);
Now we need lower siblings as below to work too:
class Orange extends Fruit implements Juicy<Orange>;
class RedOrange extends Orange;
So I would expect the method to look as follows:
<T extends Juicy<T>> List<Juice<? super T>> squeeze(List<? extends T> fruits);
Instead I find the method signature to be as below:
<**T extends Juicy<? super T>>** List<Juice<? super T>> squeezeSuperExtends(List<? extends T> fruits);
What explains this difference?
The <? super T>
within <T extends Juicy<? super T>>
is there so that RedOrange
, which is a subclass of Juicy<Orange>
can be within its bound.
Imagine without the <? super T>
first:
public <T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits) {...
Now T
must be a Juicy<T>
. The class Orange
is a Juicy<T>
, it's a Juicy<Orange>
. But the class RedOrange
is not a Juicy<T>
. It's not a Juicy<RedOrange>
; it's a Juicy<Orange>
. So when we attempt to call squeeze
:
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<RedOrange>> juices = squeeze(redOranges);
we get the following compiler error:
Inferred type 'RedOrange' for type parameter 'T' is not within its bound; should implement 'Juicy<RedOrange>'.
If we place the <? super T>
, that allows the type parameter for Juicy
to be a superclass of T
. This allows RedOrange
to be used, because it's a Juicy<Orange>
, and Orange
is a superclass to RedOrange
.
public <T extends Juicy<? super T>> List<Juice<T>> squeeze(List<T> fruits) {...
Now the call to squeeze
above compiles.
EDIT
But what if we want to squeeze
a List<Juice<Orange>>
from a List<RedOrange>
? It got a little tricky, but I found a solution:
We need a second type parameter to match Orange
in the squeeze
method:
public <S extends Juicy<S>, T extends Juicy<S>> List<Juice<S>> squeeze(List<T> fruits)
Here, S
represents Orange
, so that we can return List<Juice<Orange>>
. Now we can say
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<Orange>> juices = squeeze(redOranges);