【问题标题】: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) 这个问题起初在我看来是要我们读懂别人的想法——即为什么人们称泛型为“不变的”,而它们的一种风格不是?所以也许我太快了,但你太快了,无法对“为什么”做出一些相当不准确的假设。

标签: java generics


【解决方案1】:

List&lt;? extends Exception&gt; k = new ArrayList&lt;NumberFormatException&gt;();

这意味着列表可以采用 Exception 的子类型

不完全是。您可以分配kList——或其任何子类型,就像你有ArrayList——在这种情况下,Exception 的任何子类型。

但是你不能添加kException的任何子类型,或者任何与此相关的东西,因为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&lt;T&gt; 是不变的,因为List&lt;String&gt; 不是List&lt;Object&gt; 的子类。 “extends”关键字用于约束类型参数,但不影响方差:&lt;T&gt; void foo(List&lt;T&gt;) 表示可以将任意类型的元素列表传递给foo&lt;T extends Exception&gt; void foo(List&lt;T&gt;)意思是一样的,只是它约束了类型参数T,所以它必须是Exception的子类。

    这不会使List&lt;T&gt; 成为List&lt;Exception&gt; 的子类,它们仍然是两个不同的类。如果它是一个子类,你可以用它做与上面的数组相同的技巧:

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

    但这不会编译,因为List&lt;T&gt;不能赋值给List&lt;Exception&gt;(因为它不是子类);

    【讨论】:

      【解决方案3】:

      我认为您问题的简单答案是语义问题。
      List&lt;Object&gt; 不是List&lt;String&gt; 的超类型。 Collection&lt;String&gt; 是它的超类型,而ArrayList&lt;String&gt; 是它可能的子类型之一。

      换一种说法:

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

      【讨论】:

        猜你喜欢
        • 2010-11-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-17
        • 2014-09-14
        • 2012-01-27
        • 1970-01-01
        相关资源
        最近更新 更多