java 带有通配符的列表会导致通用伏都教错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5407995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:59:25  来源:igfitidea点击:

Lists with wildcards cause Generic voodoo error

javagenericsgeneric-listgeneric-collections

提问by oligofren

Does anyone know why the following code does not compile? Neither add() nor addAll() works as expected. Removing the "? extends" part makes everything work, but then I would not be able to add subclasses of Foo.

有谁知道为什么下面的代码不能编译?add() 和 addAll() 都没有按预期工作。删除“?扩展”部分使一切正常,但是我将无法添加 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 

error 1:

错误 1:

IntelliJ says:

IntelliJ 说:

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

The compiler says:

编译器说:

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

error 2:

错误2:

IntelliJ gives me

IntelliJ 给了我

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

Whereas the compiler just says

而编译器只是说

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);

回答by Pa?lo Ebermann

(I assume here that Barand Bazare both subtypes of Foo.)

(我在这里假设BarBaz都是 的子类型Foo。)

List<? extends Foo>means a list of elements of some type, which is a subtype of Foo, but we don't know which type. Examples of such lists would be a ArrayList<Foo>, a LinkedList<Bar>and a ArrayList<Baz>.

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

As we don't know which subtype is the type parameter, we can't put Fooobjects into it, neither Baror Bazobjects. But we still know that the type parameter is a subtype of Foo, so every element already in the list (and which we can get from the list) must be a Fooobject, so we can use Foo f = list.get(0);and similar things.

由于我们不知道哪个子类型是类型参数,因此我们不能将Foo对象放入其中,也不能放入对象BarBaz对象。但是我们仍然知道类型参数是 的子类型Foo,因此列表中的每个元素(以及我们可以从列表中获取的元素)都必须是一个Foo对象,因此我们可以使用 Foo f = list.get(0);类似的东西。

Such a list can only be used for taking elements out of the list, not to adding elements at all (apart from null, but I don't know if the compiler actually allows this).

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

A List<Foo>on the other hand allows adding any object which is a Fooobject - and as Barand Bazare subtypes of Foo, all Barand Bazobjects are Fooobjects, so they can be added, too.

List<Foo>另一方面,A允许添加任何作为对象的Foo对象 - 作为BarBaz的子类型Foo,所有BarBaz对象都是Foo对象,因此也可以添加它们。

回答by Michael Myers

Remember PECS: Producer Extends, Consumer Super.

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

Since you are trying to add items to list2, it is a consumer and cannot be declared as List<? extends Foo>. But then you are using list2 as a producer also when you add it to list1. Therefore, list2 is both a producer and a consumer and must be a List<Foo>.

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

list1, as a pure consumer, can be a List<? super Foo>.

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

回答by Vivien Barousse

They're errors. Lets modify your code, considering that Barand Bazare two different types extending Foo:

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

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

If list1.add(new Foo())was allowed, you could add Foo instances in a collection containing Bar instances. This explains the first error.

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

If list1.addAll(list2)was allowed, all instances of Baz in list2 would be added to list1, which contains only Bar instances. This explains the second error.

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

回答by Андрей Голованов

Let me try to explain in what case you might need to use <? extend Classname>.

让我试着解释一下在什么情况下您可能需要使用<? extend Classname>.

So, lets say you have 2 classes:

所以,假设你有 2 个类:

class Grand {
    private String name;

    public Grand(String name) {
        this.setName(name);
    }

    public Grand() {
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Dad extends Grand {
    public Dad(String name) {
        this.setName(name);
    }

    public Dad() {
    }
}

And lets say you have 2 collections, each contains some Grands and some Dads:

假设您有 2 个集合,每个集合都包含一些 Grands 和一些 Dads:

    List<Dad> dads = new ArrayList<>();
    dads.add(new Dad("Dad 1"));
    dads.add(new Dad("Dad 2"));
    dads.add(new Dad("Dad 3"));


    List<Dad> grands = new ArrayList<>();
    dads.add(new Dad("Grandpa 1"));
    dads.add(new Dad("Grandpa 2"));
    dads.add(new Dad("Grandpa 3"));

Now, lets asume that we want to have collection, which will contain Grand or Dad objects:

现在,假设我们想要一个包含 Grand 或 Dad 对象的集合:

        List<Grand> resultList;
        resultList = dads; // Error - Incompatable types List<Grand> List<Dad>
        resultList = grands;//Works fine

How we can avoid this? Simply use wildcard:

我们如何避免这种情况?只需使用通配符:

List<? extends Grand> resultList;
resultList = dads; // Works fine
resultList = grands;//Works fine

Notice, that you cant add new items in such (resultList) collection. For more information you can read about Wildcard and PECS conseption in Java

请注意,您不能在此类 (resultList) 集合中添加新项目。有关更多信息,您可以阅读 Java 中的通配符和 PECS 概念

回答by Tomas Narros

Sorry, maybe I misunderstood your question, but suposing:

对不起,也许我误解了你的问题,但假设:

public class Bar extends Foo{ }

this code:

这段代码:

List<Foo> list2 = new ArrayList<Foo>()
list2.add( new Bar() );

do not generate any error for me.

不要为我产生任何错误。

So, removing the wild card allows adding subclasses of Foo.

因此,删除通配符允许添加 Foo 的子类。