Limited general super method

According to the literature I read, we have juicy fruits that implement the following interface:

public interface Juicy<T> { Juice<T> squeeze(); } 

Using variables with a limited type, the following method could collect a bunch of fruits and compress them all:

 <T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits); 

Now we need younger brothers and sisters, as shown below:

 class Orange extends Fruit implements Juicy<Orange>; class RedOrange extends Orange; 

Therefore, I would expect the method to look like this:

 <T extends Juicy<T>> List<Juice<? super T>> squeeze(List<? extends T> fruits); 

Instead, I find the method signature as follows:

 <**T extends Juicy<? super T>>** List<Juice<? super T>> squeezeSuperExtends(List<? extends T> fruits); 

What explains this difference?

+7
source share
2 answers

<? super T> <? super T> inside <T extends Juicy<? super T>> <T extends Juicy<? super T>> exists so that RedOrange , which is a subclass of Juicy<Orange> , can be within it.

First imagine <<20>:

 public <T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits) {... 

Now T should be Juicy<T> . The class Orange is a Juicy<T> , a Juicy<Orange> . But the RedOrange class RedOrange not Juicy<T> . This is not a Juicy<RedOrange> ; this is a Juicy<Orange> . Therefore, when we try 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 put <? super T> <? super T> , which allows the type parameter for Juicy be a superclass of T This allows you to use RedOrange because it is Juicy<Orange> and Orange is a superclass before RedOrange .

 public <T extends Juicy<? super T>> List<Juice<T>> squeeze(List<T> fruits) {... 

Now the squeeze command call above compiles.

EDIT

But what if we want to squeeze a List<Juice<Orange>> from a List<RedOrange> ? It turned out a little more complicated, but I found a solution:

We need a parameter of the second type 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); 
+3
source

It seems to me that the easiest way to think about this is to briefly ignore the relationship between the type of fruit and the type of fruit juice that it produces. This link is set in the class declaration; we don’t need to squeeze a bunch of Juicy s.

In other words, just parameterize the Juice type, which will be << 20>:

 <T> List<Juice<T>> squeeze(List<? extends Juicy<? extends T>> fruits); 

Here we create a list of juices based on the common super-type produced by Juice (i.e. the Juicy parameter), and not on the general super-type of the fruits.

Then we get the following:

 //works List<Juice<Orange>> b = squeeze(Arrays.asList(new Orange(), new RedOrange())); //fails as it should List<Juice<RedOrange>> d = squeeze(Arrays.asList(new RedOrange())); 
+3
source

All Articles