苦苦挣扎<?在Java中扩展T> wildcard

soe*_*e18 2 java extends wildcard

我有一个非常基本的问题.

下面的代码无法编译(假设Apple Extends Fruit):

    List<? extends Fruit> numbers = new ArrayList<>();
    numbers.add(new Apple());  //compile time error
Run Code Online (Sandbox Code Playgroud)

当阅读为什么不,我理解的话,但不是概念:).

让我们假设第一个Fruit不是抽象类.我理解,因为我们正在处理多个子类型,所有这些都扩展了Fruit.据说因为我们无法确定水果的确切类型,所以我们不能在收集中加入任何东西.有几件我不明白的事情:

1)显然我们无法知道哪种水果让我感到困惑.在迭代集合时,我们不能通过typeof或其他instanceof检查告诉特定类型吗?

2)假设Fruit是一个具体的类,为什么我们不允许添加Fruit的实例?这似乎是有道理的,因为你至少知道Fruit的API.即使你不知道Fruit的确切子类型,至少你可以在Fruit()上调用标准方法.

我觉得这应该是相当明显的,但有些东西不是为了点击我.任何帮助都很感激.谢谢!

Rad*_*def 7

理解这一点的最好方法是将通配符视为对列表的一些说法,而不是结果.换一种说法:

List<Banana> allBananas = getMyBananas();
enumerateMyFruit(allBananas);

static void enumerateMyFruit(List<? extends Fruit> myFruit) {
    for (Fruit fruit : myFruit)
        System.out.println(fruit);
}
Run Code Online (Sandbox Code Playgroud)

当我们传递allBananasenumerateMyFruit方法时,我们会丢失有关列表的原始声明类型的信息.在这个例子中,我们可以非常清楚地看到为什么我们不能将苹果放入a中List<? extends Fruit>,因为我们知道列表实际上是一个List<Banana>.再一次,通配符告诉我们一些关于列表的声明类型的信息.

List<? extends Fruit>应该被解读为"最初声明要保留的列表Fruit或某些子类型Fruit,但我们不知道该声明的类型是什么".我们所知道的是,我们从列表中提取的所有内容都是Fruit.

此外,你是对的,我们可以迭代列表并instanceof用来找出列表中的真正内容,但这不会告诉我们列表的原始声明类型.在上面的代码片段中,我们会发现列表中的所有内容都是a Banana,但我可以轻松地声明allBananas为a List<Fruit>.


您可能也会看到为什么a List<Dog>不能List<Animal>解释其中的一些原因.通配符是我们如何在泛型类型之间进行协方差.a List<Dog>不是,List<Animal>但它是a List<? extends Animal>.这带来了我们无法添加到a的限制List<? extends Animal>,因为它可能是a List<Dog>,a List<Cat>或其他东西.我们不知道了.

还有? super,这是相反的.我们可以存储Fruit在一个List<? super Fruit>但我们不知道我们会从中取出什么类型的对象.它的原始声明类型实际上可能是例如a List<Object>,其中包含各种其他内容.