java 如何将元素添加到通配符泛型集合中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/176446/
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-29 11:17:36  来源:igfitidea点击:

How can elements be added to a wildcard generic collection?

javagenerics

提问by David Koelle

Why do I get compiler errors with this Java code?

为什么我会收到此 Java 代码的编译器错误?

1  public List<? extends Foo> getFoos()
2  {
3    List<? extends Foo> foos = new ArrayList<? extends Foo>();
4    foos.add(new SubFoo());
5    return foos;
6  }

Where 'SubFoo' is a concrete class that implements Foo, and Foo is an interface.

其中 'SubFoo' 是一个实现 Foo 的具体类,而 Foo 是一个接口。

Errors I get with this code:

我使用此代码遇到的错误:

  • On Line 3: "Cannot instantiate ArrayList<? extends Foo>"
  • On Line 4: "The method add(capture#1-of ? extends Foo) in the type List<capture#1-of ? extends Foo> is not applicable for the arguments (SubFoo)"
  • 第 3 行:“无法实例化 ArrayList<? extends Foo>”
  • 第 4 行:“类型 List<capture#1-of ? extends Foo> 中的方法 add(capture#1-of ? extends Foo) 不适用于参数 (SubFoo)”

Update:Thanks to Jeff C, I can change Line 3 to say "new ArrayList<Foo>();". But I'm still having the issue with Line 4.

更新:感谢 Jeff C,我可以将第 3 行更改为“new ArrayList<Foo>();”。但是我仍然遇到第 4 行的问题。

回答by erickson

Use this instead:

改用这个:

1  public List<? extends Foo> getFoos()
2  {
3    List<Foo> foos = new ArrayList<Foo>(); /* Or List<SubFoo> */
4    foos.add(new SubFoo());
5    return foos;
6  }

Once you declare foos as List<? extends Foo>, the compiler doesn't know that it's safe to add a SubFoo. What if an ArrayList<AltFoo>had been assigned to foos? That would be a valid assignment, but adding a SubFoo would pollute the collection.

一旦将 foos 声明为List<? extends Foo>,编译器就不知道添加 SubFoo 是安全的。如果ArrayList<AltFoo>已分配给foos怎么办?这将是一个有效的分配,但添加一个 SubFoo 会污染集合。

回答by Glen Best

Just thought I'd add to this old thread, by summarising the properties of List parameters instantiated with types or wildcards....

只是想通过总结用类型或通配符实例化的 List 参数的属性来添加到这个旧线程中....

When a method has a parameter/result which is a List, the use of type instantiation or wildcards determines

当一个方法的参数/结果是一个 List 时,类型实例化或通配符的使用决定了

  1. Types of List which can be passed to the method as an argument
  2. Types of List which can be populated from the method result
  3. Types of elements which can be written to list within the method
  4. Types which can be populated when reading elements from list within the method
  1. 可以作为参数传递给方法的 List 类型
  2. 可以从方法结果填充的列表类型
  3. 可以在方法中写入列表的元素类型
  4. 从方法中的列表读取元素时可以填充的类型

Param/Return type: List< Foo>

参数/返回类型: List< Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
  2. Types of List which can be populated from the method result:
    • List< Foo>
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
  3. Types of elements which can be written to list within the method:
    • Foo& subtypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo& supertypes (up to Object)
  1. 可以作为参数传递给方法的 List 类型:
    • List< Foo>
  2. 可以从方法结果中填充的列表类型:
    • List< Foo>
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
  3. 可以在方法中写入列表的元素类型:
    • Foo& 亚型
  4. 在方法中从列表中读取元素时可以填充的类型:
    • Foo& 超类型(最多Object

Param/Return type: List< ? extends Foo>

参数/返回类型: List< ? extends Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Subfoo>
    • List< SubSubFoo>
    • List< ? extends Foo>
    • List< ? extends SubFoo>
    • List< ? extends SubSubFoo>
  2. Types of List which can be populated from the method result:
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
    • List< ? extends SuperSuperFoo>
  3. Types of elements which can be written to list within the method:
    • None! Not possible to add.
  4. Types which can be populated when reading elements from list within the method:
    • Foo& supertypes (up to Object)
  1. 可以作为参数传递给方法的 List 类型:
    • List< Foo>
    • List< Subfoo>
    • List< SubSubFoo>
    • List< ? extends Foo>
    • List< ? extends SubFoo>
    • List< ? extends SubSubFoo>
  2. 可以从方法结果中填充的列表类型:
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
    • List< ? extends SuperSuperFoo>
  3. 可以在方法中写入列表的元素类型:
    • 没有任何!无法添加。
  4. 在方法中从列表中读取元素时可以填充的类型:
    • Foo& 超类型(最多Object

Param/Return type: List<? super Foo>

参数/返回类型: List<? super Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Superfoo>
    • List< SuperSuperFoo>
    • List< ? super Foo>
    • List< ? super SuperFoo>
    • List< ? super SuperSuperFoo>
  2. Types of List which can be populated from the method result:
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? super SubSubFoo>
  3. Types of elements which can be written to list within the method:
    • Foo& supertypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo& supertypes (up to Object)
  1. 可以作为参数传递给方法的 List 类型:
    • List< Foo>
    • List< Superfoo>
    • List< SuperSuperFoo>
    • List< ? super Foo>
    • List< ? super SuperFoo>
    • List< ? super SuperSuperFoo>
  2. 可以从方法结果中填充的列表类型:
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? super SubSubFoo>
  3. 可以在方法中写入列表的元素类型:
    • Foo& 超类型
  4. 在方法中从列表中读取元素时可以填充的类型:
    • Foo& 超类型(最多Object

Interpretation/Comment

解释/评论

  • needs of external callers drive the design of the method declaration i.e. the public API (normally the primary consideration)
  • needs of internal method logic drive any additional decisions re actual data types declared and constructed internally (normally the secondary consideration)
  • use List<Foo>if caller code is always focused on manipulating the Foo class, as it maximises flexibility for both read and write
  • use List<? extends UpperMostFoo>if there could be many different types of caller, focused on manipulating a different class (not always Foo) and there is a single uppermost class in the Foo type hierarchy, and if the method is to internally write to the list and caller list manipulation is reading. Here the method may internally use List< UpperMostFoo>and add elements to it, before returning List< ? extends UpperMostFoo>
  • if there could be many different types of caller, focused on manipulating a different class (not always Foo) and if reading and writing to list is required and there is a single lowest class in the Foo type hierarchy, then it makes sense to use List< ? super LowerMostFoo>
  • 外部调用者的需求推动了方法声明的设计,即公共 API(通常是主要考虑因素)
  • 内部方法逻辑的需求推动了对内部声明和构造的实际数据类型的任何额外决策(通常是次要考虑因素)
  • 使用List<Foo>如果呼叫者代码始终专注于操纵Foo类,因为它的读取和写入最大的灵活性
  • 使用List<? extends UpperMostFoo>是否可能有许多不同类型的来电显示,集中操作不同的类(并不总是富),并有在Foo类型不同层次的单一最上层阶级,如果该方法是在内部写列表和来电列表操作在看书。在这里,该方法可以List< UpperMostFoo>在返回之前在内部使用并向其添加元素List< ? extends UpperMostFoo>
  • 如果可能有许多不同类型的调用者,专注于操作不同的类(不总是 Foo),并且如果需要读取和写入列表并且 Foo 类型层次结构中有一个最低的类,那么使用它是有意义的 List< ? super LowerMostFoo>

回答by David Koelle

Try:

尝试:

public List<Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}

The generic ArrayList constructor needs to have a specific type to be parameterized on, you cannot use the '?' wildcard there. Changing the instantiation to "new ArrayList<Foo>()' would solve the first compilation error.

通用 ArrayList 构造函数需要有一个特定的类型来参数化,你不能使用 '?' 那里有通配符。将实例化更改为“new ArrayList<Foo>()”将解决第一个编译错误。

The declaration of the 'foos' variable can have wildcards, but since you know the precise type, it makes more sense to reference the same type info there. What you have now says that foos holds some specific subtype of Foo, but we don't know which. Adding a SubFoo may not be allowed, since a SubFoo is not "all subtypes of Foo". Changing the declaration to 'List<Foo> foos = ' solves the second compilation error.

'foos' 变量的声明可以有通配符,但由于您知道确切的类型,因此在那里引用相同的类型信息更有意义。您现在所说的 foos 包含 Foo 的某些特定子类型,但我们不知道是哪个。可能不允许添加 SubFoo,因为 SubFoo 不是“Foo 的所有子类型”。将声明更改为 'List<Foo> foos = ' 解决了第二个编译错误。

Finally, I would change the return type to 'List<Foo>' since clients of this method won't be able to do much with the returned value as currently defined. You should rarely use wildcards in return types. Use a parameterized method signature if needed, but prefer bounded types to only appear in method arguments, as that leaves it up to the caller who can pass in specific types and operate and them accordingly.

最后,我将返回类型更改为“List<Foo>”,因为此方法的客户端将无法对当前定义的返回值执行太多操作。您应该很少在返回类型中使用通配符。如果需要,使用参数化方法签名,但更喜欢有界类型只出现在方法参数中,因为这让调用者可以传入特定类型并相应地操作它们。

回答by zslevi

To get an idea of how generics works check out this example:

要了解泛型的工作原理,请查看以下示例:

    List<SubFoo> sfoo = new ArrayList<SubFoo>();
    List<Foo> foo;
    List<? extends Foo> tmp;

    tmp = sfoo;
    foo = (List<Foo>) tmp;

The thing is, that wasn't designed for local/member variables, but for function signatures, that's why it's so ass-backwards.

问题是,这不是为局部/成员变量设计的,而是为函数签名设计的,这就是为什么它如此落后。

回答by SCdF

The following will work fine:

以下将正常工作:

public List<? extends Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}