7

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?

IUnknown
  • 9,301
  • 15
  • 50
  • 76
  • Shouldn't that be `public interface Juicy>`? (to prevent `class Orange extends Fruit implements Juicy`) – SLaks May 07 '13 at 17:16
  • @SLaks: It's not a huge gain, as if `Apple` implemented `Juicy` then the case you mention wouldn't be prevented. – Mark Peters May 07 '13 at 17:39
  • @MarkPeters: You're right; there is no way to prevent that. However, it would prevent `class Orange extends Fruit implements Juicy` – SLaks May 07 '13 at 17:49
  • @SLaks: There's no point in "preventing" these things. They are perfectly type safe. Why do you care? – newacct May 07 '13 at 22:17

2 Answers2

3

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);
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • I like this answer, but help me think through why you *should* be able to get a `List>` from juicing a `List`. A `RedOrange` produces `Juice`, not `Juice` so wouldn't the `squeeze` method be unsatisfiable (i.e. impossible to safely/correctly implement)? – Mark Peters May 07 '13 at 17:35
  • 1
    @MarkPeters - Interesting. I've added a way to get a `List>`. – rgettman May 07 '13 at 17:46
  • My point is more that If you *can* have a declaration `List> juices = squeeze(redOranges)`, then something is definitely wrong, because a `RedOrange` does not produce `Juice`. Try safely implementing squeeze. I suspect you can't. – Mark Peters May 07 '13 at 17:52
  • Hmm, I read your edit as if you could still opt for a `List>`. I think your new solution correctly prevents that. However, `T` is superfluous since it could just be a wildcard. – Mark Peters May 07 '13 at 18:00
  • @MarkPeters, I originally attempted to get a `List>`, but you're right, that shouldn't be the goal; a `RedOrange` can only return a `Juice` when `squeeze` is called, not a `Juice`. – rgettman May 07 '13 at 18:02
3

It seems to me the easiest way to think about this is to briefly ignore the relationship between the type of fruit and the type of fruit juice it produces. That link is established in the class declaration; we don't need it to squeeze a bunch of Juicys.

In other words, just parameterize on the type of Juice that the Juicys will produce:

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

Here we produce a list of juices based on the common super type of produced Juice (i.e. the parameter of Juicy), not the common super type of fruit.

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()));
Mark Peters
  • 80,126
  • 17
  • 159
  • 190