为什么在允许“?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
.
但是你不能将添加到k
Exception
的任何子类型,或者任何与此相关的东西,因为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.
在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
.
但是你不能将添加到k
Exception
的任何子类型,或者任何与此相关的东西,因为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.