【问题标题】:Adding data to generic ? extends list [duplicate]将数据添加到泛型?扩展列表[重复]
【发布时间】:2013-01-06 12:31:44
【问题描述】:

可能重复:
Java ArrayList of ? extends Interface

有这个代码:

public static class B1 {}
public static class B2 extends B1 {}
public void fun() {
    List<? extends B1> l3 = new ArrayList<>();
    l3.add(new B2());
}

编译错误:

java: no suitable method found for add(Main.B2)
    method java.util.List.add(int,capture#1 of ? extends Main.B1) is not applicable
      (actual and formal argument lists differ in length)
    method java.util.List.add(capture#1 of ? extends Main.B1) is not applicable
      (actual argument Main.B2 cannot be converted to capture#1 of ? extends Main.B1 by method invocation conversion)

我猜? extends B1 的意思是从 B1 扩展的任何类型。好像B2类型是从B1扩展而来的,为什么这个类型的对象不能加入到列表中,怎么做才能加入呢?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    我猜? extends B1 的意思是从 B1 扩展的任何类型

    没有。它的意思是“从 B1 扩展的特定但未知的类型”。由于具体类型未知,编译器无法强制执行,因此 add 之类的操作不起作用。*

    请参阅tutorial on wildcards

    如何制作才能添加?

    基本上,不要为此使用通配符。你可能想要这个:

    List<B1> l3 = new ArrayList<B1>();
    


    * 好吧,它们确实有效,但仅适用于null(以及其他一些情况,请参阅下面的@Marko 评论)。

    【讨论】:

    • * 由于通配符捕获,它们也适用于 list.add(list.get(0)) 和类似组合。
    • @Oli 你是对的。它的答案很好,但是对于已知特定类型的分配,它可以工作。所以我发现通配符有一点差异:)。查看我的答案
    【解决方案2】:

    ? extends B1 表示:某种类型是或扩展 B1,但我们不知道是哪一种。所以该列表可以是List&lt;B1&gt;,或List&lt;B2&gt;,或List&lt;B3&gt;(假设B3 也扩展了B1)。而且您不想将 B2 添加到 List&lt;B3&gt;,因此编译器会禁止它。

    您可能想要List&lt;B1&gt;

    【讨论】:

      【解决方案3】:

      所以我猜你想要一个B1 类型的元素列表。 B1 的每个 都可以保存在 B1 的列表中,如下所示:

      public static class B1 {}
      public static class B2 extends B1 {}
      public void fun() {
          List<B1> l3 = new ArrayList<>();
          l3.add(new B2());
      }
      

      【讨论】:

        【解决方案4】:

        由于这个问题已经得到解答,但我也想在这里添加一些有价值的东西。正如大家建议的那样

        List<B1> l3 = new ArrayList<B1>();
        

        没错。但是要理解逻辑,您还应该尝试以下不会给出任何错误的逻辑。

        public void fun() {
            List<? extends B1> l3 = new ArrayList<>();
            List list1= new ArrayList();
            list1.add(Element1 extending B1);
            list1.add(Element2 extending B1);
            l3=list1;
        }
        
         or you can also try
        public void fun() {
                List<? extends B1> l3 = new ArrayList<>();
                List<B1> list1= new ArrayList();
                list1.add(B1 element);
                l3=list1;
            }
        

        因此,您的以下陈述是正确的,但对于分配而不是添加方法。简而言之,添加操作必须是真正未知的类型(如 null),但是

        ? extends B1 表示从 B1 扩展的任何类型。

        【讨论】:

        • 第一个示例应该给出编译时警告,并且不是类型安全的。绝对不推荐。
        • 我同意不建议这样做,但我的目的只是为了明确概念
        【解决方案5】:

        让 B1 和 B2 实现相同的接口,比如 B(甚至可以为空),然后改为声明列表 。您将能够毫无问题地添加和删除成员。

        直接继承的主要问题可以概括为:

        class B1 {};
        class B2 extends B1{};
        
        ArrayList<B2> list;
        
        addMembers(list); // Error here
        
        void addMembers(ArrayList<B1> list) {
          list.add(new B1());
        }
        

        这里我们声明一个 B2 的列表,并将它作为参数传递给期望 B1 列表的方法。由于每个 B2 也是 B1,所以这应该可以工作,这很直观。但是现在,在这种方法中,将 B1 也添加到传递列表中变得合法。当方法返回时,集合将被破坏,包含声明不允许的成员 B1。

        为防止这种情况,编译器将不允许使用“兼容继承类型”集合调用addMembers。我发现这是一个非常讨厌的限制,可以通过使用建议的接口轻松解决。使用通配符也能做到这一点相当复杂。你以为你可以写? extends B1

        void test() {
            ArrayList<? extends B1> list = new ArrayList();
            list.add(new B1()); // Error here
            list.add(new B2()); // Error here
            test2(list);
        }
        
        void test2(ArrayList<? extends B1> list) {
            list.add(new B1()); // Error here, the collection is read only.
        }
        

        这就是我说的,这些通配符不值得付出痛苦。而且你也可以写ArrayList&lt;? extends java.lang.Object&gt; 甚至ArrayList&lt; ? &gt; ,不会有帮助,这样的声明仍然使集合只读。

        因此,我会说,更好

        interface B {};
        class B1 implements B {};
        class B2 extends B1 implements B {};  
        
        ArrayList<B> list = new ArrayList<B>();
        list.add(new B1());
        list.add(new B2());
        test2(list);
        
        void test2(ArrayList<B> list) {
            list.add(new B1());
        }
        

        【讨论】:

        • B2 已经扩展 B1...
        • 这无关紧要。您只能将 B1 添加到 List 您只能将 B2 添加到 List (当 B2 派生自 B1 时),但您始终可以将 B 添加到 List 即使 B 是一个接口并且您可以也可以使用 B 访问共享功能,如果对象加入多个非常不同类型的集合,您还可以拥有多个推断。如果你需要在集合中使用不同的类,我真的不建议你乱用各种 <.. extends ..>
        • @AudriusMeškauskas:由于 B2 已经扩展了 B1,因此 List 可以接受 B1 的实例和 B2 的实例,因为 B2 的实例 是一个 B1。不需要B接口。你错了。自己测试一下。
        猜你喜欢
        • 1970-01-01
        • 2016-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多