javagenericspecs

bounded generic method with 'super' type


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?


Solution

  • 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);