0 java generics collections contravariance consumer
方差(尤其是逆变)问题让我头撞墙一周了。由于这里的几个问题,我终于理解了这个理论,现在一旦我开始研究它,我就会遇到我不明白的错误。
我有一个简单的类层次结构:
抽象类 Fruit,Mango 扩展 Fruit,Orange 扩展 Fruit,BloodOrange 扩展 Orange
abstract class Fruit implements PlantEatable {
private boolean isRipe;
private boolean isEatable;
public boolean isRipe() {
return isRipe;
}
public void setRipe(boolean ripe) {
isRipe = ripe;
}
@Override
public boolean isEatable() {
return isEatable;
}
public void setEatable(boolean eatable) {
isEatable = eatable;
}
}
public class Mango extends Fruit {
}
public class Orange extends Fruit{
}
public class BloodOrange extends Orange{
}
Run Code Online (Sandbox Code Playgroud)
现在,Oracle 文档总体上对泛型非常了解,除了我觉得令人困惑的最重要的部分:https ://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html
如果我正在做 PECS,即生产者扩展消费者超级
我正在尝试做一些非常简单的事情:
public class UpperBoundEg {
public static <E> void copy(List<? extends E> src, List<? super E> dest) {
src.forEach( item -> {
dest.add(item);
System.out.println("Added to dest: " + item.getClass());
});
}
public static <E> void acceptTest(List<? super Orange> objects) {
}
public static void main(String[] args) {
//Producer
List<? extends Orange> oranges = new ArrayList<>();
//oranges.add(new Orange()); // Doesn't work because its a producer ?
//Consumer
List<? super BloodOrange> objects = new ArrayList<>();
objects.add(new BloodOrange());
//objects.add(new Orange()); // Why doesn't it work ?
//objects.add(new Object()); // Why doesn't it work ?
copy(
Arrays.asList(
new Orange(),
new Orange(),
new BloodOrange(),
new Object(),
new Mango() // Why is this allowed?
),
new ArrayList<>()
);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么会出现这种情况?我以为列表<? super BloodOrange> 应该采用 BloodOrange 及其所有超级类?为什么它只接受 BloodOrange ?
为什么我可以在复制功能中添加芒果?
Run Code Online (Sandbox Code Playgroud)List<? extends Orange> oranges = new ArrayList<>(); oranges.add(new Orange()); // Doesn't work because its a producer ?
您似乎想到了一种List<? extends Orange> oranges方法:名为的变量引用的列表oranges可以包含“任何橙色或其某些子类型的东西”。
但那是错误的。
毕竟,如果这就是您想要的,您只需编写即可List<Orange>。鉴于:
BloodOrange a = new BloodOrange();
Orange b = a; // this is, obviously, legal.
Run Code Online (Sandbox Code Playgroud)
该变量的类型b是Orange,但它指向的对象实际上是 BloodOrange。这很好。所有血橙都是橙子。但是,因此,显然:
BloodOrange a = new BloodOrange();
Orange b = a;
List<Orange> list = new ArrayList<>();
list.add(b); // this.. has to be legal!
list.add(a); // it would be ridiculous if this wasn't, then!
Run Code Online (Sandbox Code Playgroud)
上面的编译完全没问题。我刚刚添加了一个血橙到List<Orange>. 当然,我可以做到:所有 BloodOranges 都是 Oranges。那么为什么我不能呢?
因此,对于“包含任何橙色或其某种子类型的列表”的含义来说,List<Orange>就是它。不是List<? extends Orange>。
List<? extends Orange>有不同的意义。它的意思是:
这是一个仅限于包含..我实际上不知道的列表。它有一些此代码未知的限制。然而,我所做的是,限制的本质是它只能包含橙子(因此,血橙也可以),或者橙子的某些子类型。
换句话说,这是合法的:
List<BloodOrange> bloodOranges = new ArrayList<BloodOrange>();
List<? extends Orange> someSortOfOrangesList = bloodOranges;
Run Code Online (Sandbox Code Playgroud)
现在您明白为什么不能.add(new Orange())调用List<? extends Orange>. 毕竟java是基于引用的,上面的代码只包含一条new语句,所以只有一个列表,句点。你只有两个变量都指向同一个列表 - 就像有一个地址簿,有两个单独的页面都列出了相同的地址 - 这并不神奇地意味着现在有两栋房子。因此,如果您向 中添加某些内容someSortOfOrangesList,那么您也会将其添加到由变量 指向的列表中bloodOranges,因为这两个变量都指向同一个列表。因此,如果您可以添加.add(new Orange())到 a List<? extends Orange>,那么我只是将 NOT BloodOrange 添加到类型为 的列表中List<BloodOrange>,然后我就破坏了它。这就是为什么编译器根本不允许你这样做。
一旦你明白了这一点,一切就都清楚了:
Run Code Online (Sandbox Code Playgroud)BloodOrange a = new BloodOrange(); Orange b = a; // this is, obviously, legal.
同样的原因:List<? super BloodOrange>并不意味着“这个列表可以包含血橙或其任何超类型”。因为那完全没有意义 - 如果你想要那样,就写List<Object>. 不,它的意思是:“这个列表有一些未知的限制,但是,我知道,那个限制要么是血橙,要么是橙子,要么是水果,要么是物体。这四个之一,我不知道是哪一个,但是..它必须是这 4 个之一,否则编译器将不允许我编写作业”。
通过该特定限制,您可以将 BloodOrange 实例添加到此列表中。因为.add(new BloodOrange())a也好List<Orange>,a也好List<BloodOrange>,a也好List<Fruit>,a也好List<Object>。我们知道它必须是这 4 个之一,所以没问题。
您不能调用.add(new Orange())此列表,因为这对于 4 种情况中的 3 种来说是可以的,但如果您的List<? super BloodOrange>变量指向类型为 的列表则不行List<BloodOrange>。因为这样你就会向其中添加一个非血橙,我们不希望这样。
Run Code Online (Sandbox Code Playgroud)BloodOrange a = new BloodOrange(); Orange b = a; List<Orange> list = new ArrayList<>(); list.add(b); // this.. has to be legal! list.add(a); // it would be ridiculous if this wasn't, then!
因为 java 会发现Object这里选择作为界限是有效的。因为它们都是物体。
这里你需要理解的一个关键的事情是,这class Foo extends Bar意味着:“任何 Foo 实例都像 Bar 一样合适,可以随时随地使用,需要 Bar;当然反之不一定成立”,因此,它们是 ALL 对象,并且可以在Object需要时使用 ALL 。因此,java 会这样: 好吧,我们将把它E绑定到 Object,将其变成需要 [A]List<? extends Object>和 [B] a List<? super Object>- 并且List<Object>适合这两个边界。因此,我们将 the 解释为List.asList(...),List.<Object>asList(...)因为它的每个参数都是一个对象(所有事物都是对象,所以这很明显),我们将解释new ArrayList<>()为new ArrayList<Object>(),瞧,它会编译得很好。因此,这就是这里发生的情况。
| 归档时间: |
|
| 查看次数: |
276 次 |
| 最近记录: |