为什么在允许“?extends Klass”时泛型被认为是不变的?

Why are generics said to be invariant when "? extends Klass" is allowed?

在Java中表示:

String[] is subtype of Object[]

所以数组被称为协变的。但对于仿制药,他们说:

List<X> will not be subType of List<Y>.

因此它是不变的。但问题是,“泛型真的是不变的吗”?

例如,如果我给出:

List<? extends Exception>

这意味着该列表可以采用 Exception 子类型 ,例如这是有效的:

List<? extends Exception> k = new ArrayList<NumberFormatException>();

那为什么说泛型是不变的呢?

List<? extends Exception> k = new ArrayList<NumberFormatException>();

this means that the list can take the subtype of Exception

不完全是。您可以 分配给 k 一个 List -- 或者它的任何子类型,因为您有 ArrayList -- 在这种情况下,任何子类型共 Exception.

但是你不能添加到kException的任何子类型,或者任何与此相关的东西,因为k是一个[= Exception 的一些未知亚型 的 14=]。例如,

k.add(new NumberFormatException());

会报错。

检索也仅限于已知类型:

NumberFormatException e1 = k.get(0); // error
Exception e2 = k.get(0); // ok, anything in k must be an Exception
NumberFormatException e3 = (NumberFormatException) k.get(0); // ok, but the usual downcast issues exist

数组在 java 中是协变的,但它们 不应该是 。这只是 java 通用语言尤其是打字系统的众多设计缺陷之一。 考虑这段代码:

public void messUp(Object objects[]) { objects[0] = "foo"; }
Integer ints[] = new Integer[] {1,2,3};
messUp(ints);

编译时没有警告,但在执行时抛出 ArrayStoreException

为了回答你的问题,List<T> 是不变的,因为 List<String> 不是 List<Object> 的子类。 "extends"关键字用于约束类型参数,但不影响方差:<T> void foo(List<T>)表示可以将任意类型的元素列表传递给foo<T extends Exception> void foo(List<T>)是同一个意思,只不过它约束了类型参数T,使得它必须是Exception的子类。

这不会使 List<T> 成为 List<Exception> 的子类,它们仍然是两个不同的 类。如果它是一个子类,你可以用它来做我上面用数组做的同样的技巧:

<T extends Exception> void foo(List<T> exceptions) { 
    List<Exception> l = exceptions;
    l.add(new RuntimeException());       
}

但这不会编译,因为List<T>不能分配给List<Exception>(因为它不是子类);

我认为您问题的简单答案是语义答案。
List<Object> 不是 List<String> 的超类型。 Collection<String> 是其超类型,而 ArrayList<String> 是其可能的子类型之一。

换句话说:

 Object[] array = new String[2]; //is a valid declaration.
 List<Object> list = new ArrayList<String>(); //is not.