【问题标题】:How can elements be added to a wildcard generic collection?如何将元素添加到通配符通用集合中?
【发布时间】:2010-09-15 15:24:42
【问题描述】:

为什么我在使用此 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 extends Foo>”
  • 第 4 行:“List 类型中的方法 add(capture#1-of ? extends Foo) 不适用于参数 (SubFoo)”

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

【问题讨论】:

    标签: java generics


    【解决方案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 声明为 List&lt;? extends Foo&gt;,编译器就不会知道添加 SubFoo 是安全的。如果 ArrayList&lt;AltFoo&gt; 已分配给 foos 怎么办?这将是一个有效的分配,但添加一个 SubFoo 会污染集合。

    【讨论】:

      【解决方案2】:

      以下将正常工作:

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

      【讨论】:

        【解决方案3】:

        试试:

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

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

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

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

        【讨论】:

        • 我不同意你的最后一点。如果调用者只需要从列表中取出东西,通配符返回类型就非常有用。
        【解决方案4】:

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

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

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

        【讨论】:

          【解决方案5】:

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

          当一个方法有一个参数/结果是一个列表时,使用类型实例化或通配符来确定

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

          参数/返回类型:List&lt; Foo&gt;

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

          参数/返回类型:List&lt; ? extends Foo&gt;

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

          参数/返回类型:List&lt;? super Foo&gt;

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

          解读/评论

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

          【讨论】:

          • 我认为List&lt;? super Foo&gt; 的 3 和 4 是错误的。它应该分别是“Foo & subtypes”和“Object”。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-14
          • 2017-01-03
          • 2019-09-21
          • 2015-08-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多