5

可能重复:
Java ArrayList 的?扩展接口

有这个代码:

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 扩展而来的,那么为什么不能将这种类型的对象添加到列表中,以及如何使其可以添加呢?

4

5 回答 5

9

我想这? extends B1意味着从 B1 扩展的任何类型

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

请参阅通配符教程

如何制作它以便可以添加?

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

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


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

于 2013-01-06T12:33:54.777 回答
2

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

你可能想要一个List<B1>.

于 2013-01-06T12:35:00.643 回答
1

所以你想要一个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());
}
于 2013-01-06T12:34:44.717 回答
0

让 B1 和 B2 实现相同的接口,比如 B(甚至可以为空),然后声明列表 <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<? extends java.lang.Object> ,甚至ArrayList< ? > ,不会有帮助,这样的声明仍然使集合只读。

因此,我会说,更好

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());
}
于 2013-01-06T12:35:30.463 回答
0

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

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;
    }

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

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

于 2013-01-06T14:21:45.217 回答