为啥当“?扩展 Klass”被允许时,泛型被认为是不变的?
Posted
技术标签:
【中文标题】为啥当“?扩展 Klass”被允许时,泛型被认为是不变的?【英文标题】:Why are generics said to be invariant when "? extends Klass" is allowed?为什么当“?扩展 Klass”被允许时,泛型被认为是不变的? 【发布时间】:2015-03-14 02:04:03 【问题描述】:在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>();
那为什么说泛型是不变的呢?
【问题讨论】:
也许,因为 String 实际上是 Object 的子类型,但 X 可能是也可能不是 Y 的子类型,因为它是通用的......只是猜测 我的评论是对您问题的回答,而不是对why a close vote?
:)
我在犹豫是否称其为重复,但我认为this question 和已接受答案中链接的 Java 教程很好地解释了这个问题。我认为造成混淆的主要原因是通配符限制了容器的类型,而不是容器元素的类型。
投票关闭的人不清楚您的要求不了解协变、不变和泛型,因此与其投票关闭,他们应该阅读这些主题并保持问题开放以供学习东西。
@LuiggiMendoza 请不要对人们的动机和理解做出假设。我是投票关闭为不清楚的人,并且(1)这个问题的原始版本以一个不完整的句子结尾,当时它没有被编辑; (2) 这个问题起初在我看来是要我们读懂别人的想法——即为什么人们称泛型为“不变的”,而它们的一种风格不是?所以也许我太快了,但你太快了,无法对“为什么”做出一些相当不准确的假设。
【参考方案1】:
List<? extends Exception> k = new ArrayList<NumberFormatException>();
这意味着列表可以采用 Exception 的子类型
不完全是。您可以分配到k
List
——或其任何子类型,就像你有ArrayList
——在这种情况下,Exception
的任何子类型。
但是你不能添加到k
Exception
的任何子类型,或者任何与此相关的东西,因为k
是一些未知子类型的List
>Exception
。例如,
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
【讨论】:
【参考方案2】:数组在 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>
(因为它不是子类);
【讨论】:
【参考方案3】:我认为您问题的简单答案是语义问题。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.
【讨论】:
以上是关于为啥当“?扩展 Klass”被允许时,泛型被认为是不变的?的主要内容,如果未能解决你的问题,请参考以下文章