Java Generics很奇怪

zzr*_*zrv 11 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这不会抛出java.lang.ClassCastException.这是为什么?

我一直在思考+System.out.println打电话toString.但是当我尝试这样做时,它会按预期抛出异常.

String sss = (Test.<Integer>abc(2)).toString();
Run Code Online (Sandbox Code Playgroud)

Ted*_*opp 12

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

有了这个声明:

String sss = (Test.<Integer>abc(2)).toString();
Run Code Online (Sandbox Code Playgroud)

这个故事有点不同.当类型参数T被替换时Object,调用代码被转换为显式地将结果转换为的字节代码Integer.就好像代码是用带签名的方法编写的,static Object abc(Object)并且语句写成:

String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();
Run Code Online (Sandbox Code Playgroud)

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

请注意该声明

String ss = "" + (Test.<Integer>abc(2));
Run Code Online (Sandbox Code Playgroud)

不需要强制转换,因为编译器只是将返回的对象反馈到对象abc()的字符串连接操作中.(有关如何完成此操作的详细信息因Java编译器而异,但它是对StringBuilderappend方法的调用,或者从Java 9开始,调用StringConcatFactory由此创建的方法.)此处的详细信息无关紧要; 关键是编译器足够聪明,可以识别不需要强制转换.

  • @KevinCruijssen编译器不关心或不了解`String.valueOf`.字符串连接编译为一系列`StringBuilder.append`.如果你明确地调用了append [可以用字符串调用](https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html#append-java.lang.String-) `toString`(可能是NPE),或者[如果没有,则使用对象](https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html#append-java. lang.Object-).在后一种情况下,`String.valueOf`由`StringBuilder`使用,但这是一个实现细节. (2认同)

Mic*_*ael 6

泛型在运行时消失,所以一个强制转换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;
...
Run Code Online (Sandbox Code Playgroud)

当你这样做

String sss = (Test.<Integer>abc(2)).toString();
Run Code Online (Sandbox Code Playgroud)

然后是字节码

...
 4: invokestatic  #3   // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
 7: checkcast     #4   // class java/lang/Integer
10: invokevirtual #5   // Method java/lang/Integer.toString:()Ljava/lang/String;
...
Run Code Online (Sandbox Code Playgroud)

您的代码在以前不存在的checkcast操作中失败.


归档时间:

查看次数:

822 次

最近记录:

7 年,6 月 前