带有通配符的列表会导致Generic voodoo错误

oli*_*ren 37 java generics generic-list generic-collections

有谁知道为什么下面的代码不能编译?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 
Run Code Online (Sandbox Code Playgroud)

错误1:

IntelliJ说:

add(capture<? extends Foo>) in List cannot be applied to add(Foo)
Run Code Online (Sandbox Code Playgroud)

编译器说:

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

错误2:

IntelliJ给了我

addAll(java.util.Collection<? extends capture<? extends Foo>>) in List cannot be applied to addAll(java.util.List<capture<? extends Foo>>)
Run Code Online (Sandbox Code Playgroud)

而编译器只是说

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);
Run Code Online (Sandbox Code Playgroud)

Paŭ*_*ann 55

(我这里假设BarBaz是两种亚型Foo.)

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

由于我们不知道哪个亚型是类型参数,我们不能把Foo对象进去,既不BarBaz对象.但我们仍然知道type参数是一个子类型Foo,因此列表中已经存在的每个元素(以及我们可以从列表中获取的元素)必须是一个Foo对象,所以我们可以使用 Foo f = list.get(0);和类似的东西.

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

List<Foo>另一方面允许添加任何对象,它是一个Foo对象-与作为BarBaz是的亚型Foo,所有BarBaz对象是Foo对象,所以它们可以被添加,太.


Mic*_*ers 25

记住PECS:Producer扩展,消费者超级.

由于您尝试将项目添加到list2,因此它是一个使用者,无法声明为List<? extends Foo>.但是当你将list2添加到list1时,你也将list2用作生产者.因此,list2既是生产者又是消费者,必须是a List<Foo>.

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


Viv*_*sse 8

他们是错误的.让我们修改您的代码,考虑到BarBaz正在扩展两种不同类型Foo:

List<? extends Foo> list1 = new ArrayList<Bar>();
List<? extends Foo> list2 = new ArrayList<Baz>();
Run Code Online (Sandbox Code Playgroud)

如果list1.add(new Foo())允许,您可以在包含Bar实例的集合中添加Foo实例.这解释了第一个错误.

如果list1.addAll(list2)允许,list2中的所有Baz实例都将添加到list1,其中只包含Bar实例.这解释了第二个错误.