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

2022-09-02 05:10:20

为什么使用此 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  }

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

我用这个代码得到的错误:

  • 在第 3 行:“无法实例化 ArrayList<?扩展 Foo>”
  • 在第 4 行:“方法 add(capture#1-of ?扩展 Foo) 类型 List<capture#1-of ?扩展 Foo>不适用于参数 (SubFoo)”

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


答案 1

请改用以下内容:

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  }

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


答案 2

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

当方法具有作为 List 的参数/结果时,使用类型实例化或通配符可确定

  1. 可作为参数传递给方法的列表类型
  2. 可从方法结果填充的列表类型
  3. 可以编写以在方法中列出的元素的类型
  4. 从方法内的列表中读取元素时可以填充的类型

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

  1. 可以作为参数传递给方法的列表类型:
    • List< Foo>
  2. 可从方法结果填充的列表类型:
    • List< Foo>
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
  3. 可以编写以在方法中列出的元素类型:
    • Foo和亚型
  4. 从方法中的列表中读取元素时可以填充的类型:
    • Foo和超类型(最多Object)

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

  1. 可以作为参数传递给方法的列表类型:
    • 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)

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

  1. 可以作为参数传递给方法的列表类型:
    • 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)

解释/评论

  • 外部调用者的需求推动了方法声明的设计,即公共API(通常是主要考虑因素)
  • 内部方法逻辑的需求推动了内部声明和构造的实际数据类型的任何其他决策(通常是次要考虑因素)
  • 如果调用方代码始终专注于操作 Foo 类,则使用,因为它可以最大限度地提高读取和写入的灵活性List<Foo>
  • 如果可能有许多不同类型的调用方,则使用,专注于操作不同的类(并不总是Foo),并且在Foo类型层次结构中有一个最上面的类,并且如果方法是在内部写入列表并且调用方列表操作正在读取。在这里,该方法可以在返回之前在内部使用并向其添加元素List<? extends UpperMostFoo>List< UpperMostFoo>List< ? extends UpperMostFoo>
  • 如果可以有许多不同类型的调用方,专注于操作不同的类(并不总是Foo),并且如果需要读取和写入列表,并且Foo类型层次结构中有一个最低的类,那么使用它是有意义的List< ? super LowerMostFoo>

推荐