根据我阅读的文献,我们有多汁的水果实现以下接口:
public interface Juicy<T> {
Juice<T> squeeze();
}
使用有界类型变量,下面的方法将获取一堆水果并将它们全部挤压:
<T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits);
现在我们需要下面的兄弟姐妹也工作:
class Orange extends Fruit implements Juicy<Orange>;
class RedOrange extends Orange;
所以我希望这个方法看起来像这样:
<T extends Juicy<T>> List<Juice<? super T>> squeeze(List<? extends T> fruits);
相反,我发现方法签名如下:
<**T extends Juicy<? super T>>** List<Juice<? super T>> squeezeSuperExtends(List<? extends T> fruits);
如何解释这种差异?
<T extends Juicy<? super T>>
内的<? super T>
存在,使得Juicy<Orange>
的子类RedOrange
在其界内
想象没有<? super T>
的场景:
public <T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits) {...
现在T
必须是Juicy<T>
。类Orange
是Juicy<T>
,它是Juicy<Orange>
。但是类RedOrange
不是类Juicy<T>
。这不是Juicy<RedOrange>
;这是Juicy<Orange>
。所以当我们尝试调用squeeze
:
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<RedOrange>> juices = squeeze(redOranges);
得到以下编译错误:
Inferred type 'RedOrange' for type parameter 'T' is not within its bound; should implement 'Juicy<RedOrange>'.
如果放置<? super T>
,则允许Juicy
的类型形参是T
的超类。这允许使用RedOrange
,因为它是Juicy<Orange>
,而Orange
是RedOrange
的超类。
public <T extends Juicy<? super T>> List<Juice<T>> squeeze(List<T> fruits) {...
现在对squeeze
的调用编译了。
编辑
但是,如果我们想从List<RedOrange>
中获得squeeze
和List<Juice<Orange>>
呢?这有点棘手,但我找到了一个解决方案:
需要第二个类型参数来匹配squeeze
方法中的Orange
:
public <S extends Juicy<S>, T extends Juicy<S>> List<Juice<S>> squeeze(List<T> fruits)
这里,S
代表Orange
,所以我们可以返回List<Juice<Orange>>
。现在我们写入
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<Orange>> juices = squeeze(redOranges);
在我看来,考虑这个问题最简单的方法是简单地忽略水果类型和它产生的果汁类型之间的关系。这个链接是在类声明中建立的;我们不需要它来挤压一堆Juicy
s。
换句话说,只需参数化Juicy
s将产生的Juice
的类型:
<T> List<Juice<T>> squeeze(List<? extends Juicy<? extends T>> fruits);
这里,我们根据所生产的Juice
的共同超类型(即Juicy
的参数),而不是共同超类型的水果,生成果汁列表。
然后我们得到以下内容:
//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()));