带有通配符的列表会导致通用巫毒教错误

有谁知道为什么下面的代码不能编译?add() 和 addAll() 都不能按预期工作。删除“?extends“部分使一切正常,但这样我就无法添加Foo的子类。

 List<? extends Foo> list1 = new ArrayList<Foo>();
 List<? extends Foo> list2 = new ArrayList<Foo>();

 /* Won't compile */
 list2.add( new Foo() ); //error 1
 list1.addAll(list2);    //error 2 

错误 1:

IntelliJ 说:

add(capture<? extends Foo>) in List cannot be applied to add(Foo)

编译器说:

cannot find symbol
symbol  : method addAll(java.util.List<capture#692 of ? extends Foo>)
location: interface java.util.List<capture#128 of ? extends Foo>

错误 2:

IntelliJ给我

addAll(java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(java.util.List<capture<? extends Foo>>)

而编译器只是说

cannot find symbol
symbol  : method addAll(java.util.List<capture#692 of ? extends Foo>)
location: interface java.util.List<capture#128 of ? extends Foo>
        list1.addAll(list2);

答案 1

(我在这里假设 和 都是 的子类型。BarBazFoo

List<? extends Foo>表示某种类型的元素列表,这是 Foo 的子类型,但我们不知道是哪种类型。此类列表的示例包括 a、a 和 。ArrayList<Foo>LinkedList<Bar>ArrayList<Baz>

由于我们不知道哪个子类型是类型参数,因此我们无法将对象放入其中,既不是对象,也不是对象。但是我们仍然知道 type 参数是 的子类型,因此列表中已经存在的每个元素(我们可以从列表中获得的元素)都必须是一个对象,因此我们可以使用和类似的东西。FooBarBazFooFooFoo f = list.get(0);

这样的列表只能用于从列表中取出元素,而不能用于添加元素(除了,但我不知道编译器是否真的允许这样做)。null

另一方面,A 允许添加任何对象 ,该对象是 对象 - 并且 as 和 是 的子类型,所有 和 对象都是对象,因此也可以添加它们。List<Foo>FooBarBazFooBarBazFoo


答案 2

记住PECS:生产者扩展,消费者超级

由于您正在尝试向 list2 添加项,因此它是使用者,不能声明为 。但是,当您将 list2 添加到 list1 时,您也使用 list2 作为生产者。因此,list2 既是生产者又是消费者,并且必须是 .List<? extends Foo>List<Foo>

list1,作为一个纯粹的消费者,可以是一个.List<? super Foo>


推荐