一尘不染

Java Generics进行奇怪的转换

java

这个问题已经在这里有了答案

为什么对泛型的这种使用不会引发运行时或编译时异常? (3个答案)

2年前关闭。

我正在使用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.println打电话toString。但是,当我尝试这样做时,它会按预期抛出异常。

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

阅读 156

收藏
2020-12-03

共1个答案

一尘不染

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

带有以下语句:

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

故事有点不同。当type参数T替换为时Object,调用代码将转换为字节代码,该字节代码将结果显式转换为Integer。就像代码是使用带有签名的方法编写的,static Object abc(Object)并且语句是这样编写的:

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

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

注意声明

String ss = "" + (Test.<Integer>abc(2));

不需要强制转换,因为编译器只是将返回的对象提供给对象abc()的字符串连接操作。(有关此操作的详细信息随Java编译器的不同而不同,但这是对StringBuilderappend方法的调用,或者,从Java
9开始,是对StringConcatFactory。创建的方法的调用。)这里的细节无关紧要;关键是编译器足够聪明,可以识别出不需要强制转换。

2020-12-03