21

这是一个代码片段:

import java.util.*;
class Test
{
    public static void main(String[] args)
    {
        List<Integer> list = new ArrayList<>();
        addToList(list);
        Integer i = list.get(0); //#1 fails at run-time
        String s = list.get(0); //#2 fails at compile-time
        list.get(0); //#3 works fine
        System.out.println(list.get(0)); //#4 works fine, prints "string"
    }
    static void addToList(List list){
        list.add("string");
    }
}

我明白为什么可以在参数化列表中插入 String 类的对象。

似乎我理解为什么标记为#1#2失败的代码。

但为什么要做#3#4工作?据我了解,编译器在类型擦除后添加了适当的强制转换,因此当我调用 时list.get(0),此方法应返回先前强制转换为 Integer 的 Object。那么为什么在运行时 #3 和 #4 没有发生 ClassCastException 呢?

4

5 回答 5

22

#3 有效,因为返回的对象get(int)被忽略。返回存储在位置0的任何内容,但由于没有强制转换,因此不会发生错误。

出于同样的原因,#4 可以正常工作:由 生成的对象get(0)被视为java.lang.Object子类 in println,因为toString被调用。由于toString()可用于所有 Java 对象,因此调用完成时不会出现错误。

于 2015-03-04T16:31:13.610 回答
8

首先是您可以将字符串添加到List<Integer>. 在方法中

static void addToList(List list){

您使用原始类型。原始类型的存在纯粹是为了与旧 Java 版本兼容,不应在新代码中使用。在该addToList方法中,Java 编译器不知道list应该只包含整数,因此在向其中添加 String 时它不会报错。

至于你两个陈述的不同行为。Integer i = list.get(0)在编译时不会失败,因为 Java 认为list只包含Integers。只有在运行时才会发现 的第一个元素list不是整数,因此你会得到一个ClassCastException.

String s = list.get(0)在编译时失败,因为 Java 编译器假定list仅包含整数,因此它假定您尝试将整数分配给字符串引用。

只是list.get(0)不存储方法调用的结果。因此,无论是在编译时还是在运行时,都没有任何失败的原因。

最后,System.out.println(list.get(0))work because System.outis aPrintStream并且有一个println(Object)方法,可以用Integer参数调用。

于 2015-03-04T16:33:32.667 回答
5

如果你看ArrayList#get方法。就是这个:

public E get(int index) {
   //body
}

但在运行时它实际上是:

public Object get(int index) {
       //body
}

因此,当您这样做时Integer i = list.get(0); ,编译器会将其转换为:

Integer i = (Integer)list.get(0);

现在在运行时,list.get(0)返回一个Object类型(实际上是String)。它现在尝试转换String => Integer,但失败了。

3

因为它只是:

list.get(0)

编译器确实为任何东西添加了类型转换。所以它只是,list.get(0)

4

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

list.get(0)返回一个Object类型。所以public void println(Object x)PrintStream 的方法被调用。

请记住println(Object x)被调用而不是println(String x)

于 2015-03-05T07:23:06.150 回答
2

4:重载 System.out.println(Object) 被调用,因为 Integer <=_T Object(阅读:Integer is-a Object)。请注意, list.get(int) 在运行时返回一个 Object ,因为类型参数被删除了。现在阅读

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

这告诉您“如果需要,请插入类型转换以保持类型安全。”,因为从 Object 到 Object 不需要类型转换,因此不会导致 ClassCastException。

出于同样的原因,在 3 处没有类型转换,但是方法调用的副作用可能会发生,而 List.get 没有。

于 2015-03-04T16:37:26.033 回答
1

强制转换应用于 的返回类型get,而不是add引入污染的 。否则,此时您将收到编译时错误或异常,因为您无法将 a 强制String转换为Integer.

于 2015-03-04T16:29:36.123 回答