为什么可以从Java中的参数化列表中找回"错误类型"的对象?

fro*_*SPb 22 java generics raw-types parameterized-types

这是一段代码片段:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

我理解为什么可以在参数化列表中插入String类的对象.

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

但是,为什么#3#4工作?据我所知,编译器在类型擦除后添加了适当的强制转换,因此当我调用时list.get(0),此方法应该返回一个先前转换为Integer的Object.那么为什么在运行时没有ClassCastException出现在#3和#4?

das*_*ght 23

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

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

  • +1.JLS实际上没有指定"铸造"本身就发生了; 相反,它指定赋值转换([§5.2](http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.2))和方法调用转换([ §5.3](http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.3))如果实例的运行时类型与目标不匹配,则可以引发ClassCastExceptions类型.OP的#3和#4不会触发这些规则中的任何一个,因此没有ClassCastException. (10认同)
  • @fromSPb是的.除非您将值分配给需要强制转换的内容,否则不会强制转换为"整数". (2认同)

Hoo*_*pje 9

首先,你可以添加一个字符串的原因List<Integer>.在方法中

static void addToList(List list){
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

最后,System.out.println(list.get(0))work是因为System.out是a PrintStream并且有一个println(Object)方法,可以用Integer参数调用.


Jat*_*tin 6

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

public E get(int index) {
   //body
}
Run Code Online (Sandbox Code Playgroud)

但在运行时它实际上是:

public Object get(int index) {
       //body
}
Run Code Online (Sandbox Code Playgroud)

所以当你这样做时Integer i = list.get(0); 编译器将它转换为:

Integer i = (Integer)list.get(0);
Run Code Online (Sandbox Code Playgroud)

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

3

因为它只是:

list.get(0)
Run Code Online (Sandbox Code Playgroud)

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

4

System.out.println(list.get(0));
Run Code Online (Sandbox Code Playgroud)

list.get(0)返回一个Object类型.因此public void println(Object x)调用PrintStream的方法.

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