【问题标题】:Java generics - mixing types allowed?Java泛型 - 允许混合类型?
【发布时间】:2011-12-08 16:09:24
【问题描述】:

我之前运行了一些测试,但找不到解释为什么这段代码会这样做:

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList(Arrays.asList(Double.valueOf(0.1234)));
        System.out.println(list.get(0)); //prints 0.1234
        Object d = list.get(0);
        System.out.println(d.getClass()); // prints class java.lang.Double
        System.out.println(list.get(0).getClass()); // ClassCastException
    }
}

这引发了几个问题:

  • 为什么 List 首先接受一个 Double (它应该编译吗)?
  • 为什么第二个打印工作而不是第三个打印工作,虽然看起来他们在做同样的事情?

编辑
我理解以下两种说法:

List aList = new ArrayList(); //I can add any objects in there
List<Integer> aList = new ArrayList<Integer>(); //I can only add something that extends Integer in there

但我不明白为什么这个是授权的,以及为什么它在运行时实际上在某种程度上有效,尽管某些操作会产生 ClassCastException - 我本来希望在上面发布的代码的第一行出现 ClassCastException:

List<Integer> aList = new ArrayList(); //I can any objects in there

【问题讨论】:

  • 在您的编辑中:这有效,因此仍然支持泛型之前的 java 版本。 (Java 在向后兼容方面付出了很多努力。)
  • 经过一番思考,我在下面修改了我的答案。我认为它现在更准确地回答了为什么它会以这种方式工作。

标签: java generics


【解决方案1】:

这个:

new ArrayList(Arrays.asList(Double.valueOf(0.1234)))

创建一个原始(无类型)ArrayList,您可以在其中放置任何东西。这是正确的做法:

new ArrayList<Integer>(Arrays.asList(Double.valueOf(0.1234)))

现在不应该编译。

【讨论】:

  • 我知道正确的表格。我只是好奇为什么我发布的代码虽然被声明为 List <Integer> 却能编译。以及为什么它在运行时为某些操作而不是其他操作发送 ClassCastException。
  • 那么这是编译器中的一个漏洞,允许您将ArrayList 放入ArrayList&lt;Integer&gt; 中吗?还是出于某种原因允许这样做?
  • @Toomai:在引入泛型之前,这是为了支持遗留代码。见en.wikipedia.org/wiki/…
  • 另外应该注意的是,编译原始程序会在 list 被赋值的那一行产生一个未经检查的调用的警告。基本上,如果您使用泛型,则保证您的代码不会抛出 ClassCastException只有在没有任何关于未检查强制转换的警告的情况下编译所有内容
  • 基本上你必须记住,在 Java 中所有泛型都只是 X ,编译器会在其中添加适当的转换。因此,如果您创建一个ArrayList&lt;Integer&gt;,它仍然只是旧式的ArrayList 对象,编译器会为您进行强制转换。因此,您可以创建 ArrayList&lt;Integer&gt; 演员表或将其分配给 ArrayList&lt;Object&gt; 并照此处理。
【解决方案2】:

如果你写

... new ArrayList<Integer>(...

反而会导致编译器异常。

关于它的工作原理:

System.out.println(list.get(0)); //prints 0.1234

Object.toString() 方法在 DoubleInteger 中是相同的(并且因为 System.out.println() 需要一个 Object,所以不会将其转换为 Integer(编译器优化了转换))

Object d = list.get(0);
System.out.println(d.getClass()); // prints class java.lang.Double

.getClass() 也是如此。在这里,优化器再次放弃了演员表。

System.out.println(list.get(0).getClass()); // ClassCastException

这实际上从列表中创建了一个 Integer 并且失败了。它进行强制转换是因为优化器认为它需要这样做,因为它不需要这样做并不明显。

如果您将最后一行更改为:

    System.out.println(((Object)list.get(0)).getClass());

它有效:)

【讨论】:

  • 所以 list.get(0) 被转换为 Object 并且第二个打印语句有效,但是 list.get(0).getClass() 中的 list.get(0) 被转换为 Integer 并且失败了在第三个打印语句中。
  • @assylias 基本上是的,但你必须明白,工作的部分只是因为编译器优化。类转换是有代价的,当编译器发现它不必要时(因为明确请求了一个对象),它就会丢弃它。
【解决方案3】:

第二个不起作用,因为当您使用泛型时,编译器会为您插入强制转换(您不必这样做)。编译器尝试将元素转换为 Integer,因为这是列表的通用类型。

由于您通过未选中的添加添加到列表中,因此列表中的某些内容在进入时未选中,因此在出来时失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-04
    • 2019-01-25
    • 2016-07-12
    • 2018-08-21
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 2013-12-26
    相关资源
    最近更新 更多