Java泛型转换很奇怪

时间:2018-11-06 15:37:55

标签: java generics casting

我正在使用Java 8。

我最近遇到了这个问题:

public class Test {
    public static void main(String[] args) {
        String ss = "" + (Test.<Integer>abc(2));
        System.out.println(Test.<Integer>abc(2));
    }
    public static <T> T abc(T a) {
        String s = "adsa";
        return (T) s;
    }
}

这不会引发java.lang.ClassCastException。为什么会这样?

我一直以为+System.out.printlntoString。但是当我尝试这样做时,它会按预期抛出异常。

String sss = (Test.<Integer>abc(2)).toString();

2 个答案:

答案 0 :(得分:12)

它不会抛出ClassCastException,因为所有通用类型信息都已从编译后的代码中剥离(称为type erasure的过程)。基本上,任何类型参数都由Object代替。这就是第一个版本起作用的原因。这也是代码完全编译的原因。如果您要求编译器使用-Xlint:unchecked标志警告未经检查或不安全的操作,则会在return的{​​{1}}语句中收到有关未经检查的强制转换的警告。

使用以下语句:

abc()

故事有点不同。当类型参数String sss = (Test.<Integer>abc(2)).toString(); 替换为T时,调用代码将转换为字节码,该字节码将结果显式转换为Object。好像代码是使用带有签名Integer的方法编写的,并且语句是这样编写的:

static Object abc(Object)

也就是说,String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString(); 内部的转换不仅会由于类型擦除而消失,而且编译器还会在调用代码中插入新的转换。该强制类型转换在运行时生成abc(),因为从ClassCastException返回的对象是abc(),而不是String

请注意,声明

Integer

不需要强制转换,因为编译器只是将String ss = "" + (Test.<Integer>abc(2)); 返回的对象馈送到对象的字符串连接操作中。 (有关此操作的详细信息,随Java编译器的不同而不同,但是它要么是对abc()附加方法的调用,要么是从Java 9开始,是对StringBuilder创建的方法的调用。)这里的细节无关紧要;关键是编译器足够聪明,可以识别出不需要强制转换。

答案 1 :(得分:6)

泛型在运行时消失,因此对import matplotlib.pyplot as plt # Pie chart, where the slices will be ordered and plotted counter-clockwise: labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' sizes = [15, 30, 45, 10] explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs') fig1, ax1 = plt.subplots() ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90) ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle. plt.show() 的强制转换实际上只是对T的强制转换(编译器实际上将摆脱它),因此没有类强制转换异常。

Object只是一种获取对象并返回对象的方法。 abc是被调用的方法,从字节码可以看出:

StringBuilder.append(Object)

完成时

...  
16: invokestatic  #7   // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
19: invokevirtual #8   // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
...

然后字节码是

String sss = (Test.<Integer>abc(2)).toString();

您的代码在checkcast operation处失败,该密码以前不存在。