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?
In order to use a generic type we must provide one type argument per type parameter that was declared for the generic type. The type argument list is a comma separated list that is delimited by angle brackets and follows the type name. The result is a so-called parameterized type.
If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class. These are known as bounded-types in generics in Java.
Multiple parametersYou can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.
A type parameter can have multiple bounds.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With