列表中的通配符 - Java泛型

Ofe*_*Ron 2 java generics

ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());
Run Code Online (Sandbox Code Playgroud)

为什么这不编译?

shl*_*i33 5

Java 教程的相关部分(http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html):

List 定义的列表可以非正式地认为是只读的,但这并不是严格的保证。假设您有以下两个类:

class NaturalNumber {

    private int i;

    public NaturalNumber(int i) { this.i = i; }
    // ...
}

class EvenNumber extends NaturalNumber {

    public EvenNumber(int i) { super(i); }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

考虑以下代码:

List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35));  // compile-time error
Run Code Online (Sandbox Code Playgroud)

因为List<EvenNumber>是 的子类型List<? extends NaturalNumber>,您可以将 le 分配给 ln。但是您不能使用 ln 将自然数添加到偶数列表中。

可以对列表进行以下操作:

  • 您可以添加空值。
  • 你可以调用clear。
  • 您可以获取迭代器并调用remove。
  • 您可以捕获通配符并写入从列表中读取的元素。

您可以看到 List 定义的列表不是严格意义上的只读,但您可能会这样认为,因为您无法在列表中存储新元素或更改现有元素。

另一个相关的解释可以在这里找到(他的链接也解释了通配符的问题 - http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html):

...

但是,向其中添加任意对象是不安全的:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Run Code Online (Sandbox Code Playgroud)

因为我们不知道 c 的元素类型代表什么,所以我们不能向它添加对象。add() 方法接受类型为 E 的参数,即集合的元素类型。当实际类型参数为 ? 时,它代表某种未知类型。我们传递给 add 的任何参数都必须是这种未知类型的子类型。因为我们不知道那是什么类型,所以我们不能传入任何东西。唯一的例外是 null,它是所有类型的成员。

另一方面,给定一个 List,我们可以调用 get() 并使用结果。结果类型是未知类型,但我们始终知道它是一个对象。因此,将 get() 的结果分配给类型为 Object 的变量或将其作为参数传递给类型为 Object 的参数是安全的。