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

时间:2008-10-06 22:22:12

标签: 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  }

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

我在这段代码中遇到的错误:

  • 第3行:“无法实例化ArrayList&lt;?extends Foo&gt;”
  • 第4行:“方法添加(捕获#1-of?extends Foo)类型列表&lt; capture#1-of?extends Foo&gt;不适用于参数(SubFoo)”

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

5 个答案:

答案 0 :(得分: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  }

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

答案 1 :(得分:15)

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

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

  1. 可以作为参数传递给方法的List类型
  2. 可以从方法结果中填充的列表类型
  3. 可以在方法中列出的元素类型
  4. 在方法中从列表中读取元素时可以填充的类型
  5. 参数/返回类型:List< Foo>

    1. 可以作为参数传递给方法的List类型:
      • List< Foo>
    2. 可以从方法结果填充的List类型:
      • List< Foo>
      • List< ? super Foo>
      • List< ? super SubFoo>
      • List< ? extends Foo>
      • List< ? extends SuperFoo>
    3. 可以在方法中写入列表的元素类型:
      • Foo&amp;亚型
    4. 在方法中从列表中读取元素时可以填充的类型:
      • Foo&amp;超类型(最多Object
    5. 参数/返回类型: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&amp;超类型(最多Object
      5. 参数/返回类型: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&amp;超型
        4. 在方法中从列表中读取元素时可以填充的类型:
          • Foo&amp;超类型(最多Object
        5. 解读/注释

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

答案 2 :(得分:6)

尝试:

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

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

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

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

答案 3 :(得分:2)

以下工作正常:

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

答案 4 :(得分:1)

要了解泛型如何工作,请查看此示例:

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

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

问题是,这不是为本地/成员变量而设计的,但对于功能签名而言,这就是为什么它如此向后倾斜。