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

Dav*_*lle 16 java generics

为什么我用这个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  }
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在第3行:"无法实例化ArrayList <?extends Foo>"
  • 在第4行:"方法添加(捕获#1-of?extends Foo)在类型List <capture#1-of?extends Foo>中不适用于参数(SubFoo)"

更新:感谢Jeff C,我可以更改第3行来说"new ArrayList <Foo>();".但我仍然遇到第4行的问题.

eri*_*son 29

请改用:

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

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


Gle*_*est 15

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

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

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

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

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

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

  1. 可以作为参数传递给方法的List类型:
    • List< Foo>
    • List< Subfoo>
    • List< SubSubFoo>
    • List< ? extends Foo>
    • List< ? extends SubFoo>
    • List< ? extends SubSubFoo>
  2. 可以从方法结果填充的List类型:
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
    • List< ? extends SuperSuperFoo>
  3. 可以在方法中写入列表的元素类型:
    • 没有!无法添加.
  4. 在方法中从列表中读取元素时可以填充的类型:
    • Foo和超类型(最多Object)

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

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

口译/评论

  • 外部调用者的需求驱动方法声明的设计,即公共API(通常是主要考虑因素)
  • 内部方法逻辑的需求驱动内部声明和构造的实际数据类型的任何其他决策(通常是次要考虑因素)
  • 使用List<Foo>如果呼叫者代码始终专注于操纵Foo类,因为它的读取和写入最大的灵活性
  • 使用List<? extends UpperMostFoo>是否可能有许多不同类型的来电显示,集中操作不同的类(并不总是富),并有在Foo类型不同层次的单一最上层阶级,如果该方法是在内部写列表和来电列表操作在看书.List< UpperMostFoo>在返回之前,该方法可以在内部使用并向其添加元素List< ? extends UpperMostFoo>
  • 如果可能有许多不同类型的调用者,专注于操作不同的类(并不总是Foo),如果需要读取和写入列表,并且在Foo类型层次结构中有一个最低级别,那么使用它是有意义的 List< ? super LowerMostFoo>

  • 我认为`List&lt;? 超级 Foo&gt;` 是错误的。它应该分别是“Foo &amp; subtypes”和“Object”。 (2认同)

小智 7

尝试:

public List<Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 我不同意你的最后一点.如果调用者只需要从列表中取出内容,则通配符返回类型非常有用. (2认同)