如何以及何时将变量转换为在泛型类型参数中声明的类型?

时间:2018-03-30 11:07:10

标签: java generics

public static void main(String[] args) {

    List<Integer> integers = new ArrayList<>();
    integers.add(5); //element #0

    List list = integers;
    list.add("foo"); //element #1

    integers.get(1); //no error
    System.out.println(integers.get(1)); //no error, prints "foo"
    Integer i = integers.get(1); //throws ClassCastException

}

我试图理解投射类型变量的过程,声明为泛型类型参数,我有点困惑。 因此,您可以在我提供的示例中看到,在我们创建非参数化List之后,它引用List<Integer>的同一个对象,然后我们可以添加任何对象列表(好的,这里没什么可惊讶的),令我困惑的是,我们可以从Integer中提取非List<Integer> integers值。为什么在ClassCastException的第一次或第二次调用中没有integers.get(1)被抛出?

我假设返回参数类型的方法实际上总是返回Object,并且隐式尝试将这些返回值转换为 运行时的l值类型或方法参数类型(因为运行时没有泛型),但是以下测试使我确信Integer始终优先于Object

public static void main(String[] args) {

    List<Integer> integers = new ArrayList<>();
    integers.add(5); //element #0

    List list = integers;
    list.add("foo"); //element #1

     print(integers.get(1));

}

private static void print(Object var) {
    System.out.println(var);
}

//this method is entered
private static void print(Integer var) { 
    System.out.println(var);
}

private static void print(String var) {
    System.out.println(var);
}

另一个有趣的事实是,虽然ArrayList的元素存储在Object[]数组中,但它们总是在方法get()中返回之前转换为类型参数中定义的类型:

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

E elementData(int index) {
    return (E) elementData[index];
}

所以,如果有人可以指出我一步一步解释这些问题的文件,我会非常感激

1 个答案:

答案 0 :(得分:2)

编译器在需要强制转换时插入强制转换。方法System.out.println的参数类型为Object,因此不需要转化为Integer

对于三个print方法,选择了类型为Integer的参数的方法,因此编译器会插入一个强制转换。根据一组复杂的规则,选择使用三种方法中的哪一种在编译时间。这些规则使用通用信息来查看integers.get(1)类型为Integer,因此选择Integer版本并需要强制转换。因此,代码或多或少等同于Java 4代码

List integers = new ArrayList();
integers.add("foo");
integers.add(Integer.valueOf(5));  // No autoboxing in Java 4!
print((Integer) integers.get(1));  // Cast inserted by compiler

在问题的最后部分中转换为(E)实际上并没有在运行时执行任何操作,因此不会抛出ClassCastException。只需要编译代码即可。您告诉编译器,是的,您确定Object实际上是E并且稍后不会导致异常(尽管您通过混合原始类型和泛型类型来颠覆它)。 / p>