Java泛型奇怪地强制转换
我正在使用Java8 我最近遇到了这样一个问题:Java泛型奇怪地强制转换,java,generics,casting,Java,Generics,Casting,我正在使用Java8 我最近遇到了这样一个问题: 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) {
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;
}
}
它不会抛出
ClassCastException
,因为所有泛型类型信息都是从编译代码中剥离出来的(一个名为。基本上,任何类型参数都将替换为对象
。这就是为什么第一个版本有效。这也是代码编译的原因。如果您要求编译器使用-Xlint:unchecked
标志警告未经检查或不安全的操作,您将在abc()
的return
语句中收到未经检查强制转换的警告
谨此声明:
String sss = (Test.<Integer>abc(2)).toString();
也就是说,不仅abc()
中的强制转换由于类型擦除而消失,编译器还会在调用代码中插入一个新的强制转换。此强制转换在运行时生成一个ClassCastException
,因为从abc()
返回的对象是字符串,而不是整数
请注意,该声明
String ss = "" + (Test.<Integer>abc(2));
String ss=“”+(Test.abc(2));
不需要强制转换,因为编译器只是将abc()
返回的对象馈送到对象的字符串连接操作中。(如何完成的细节因Java编译器而异,但它要么是对StringBuilder
append方法的调用,要么是对StringConcatFactory
创建的方法的调用)这里的细节无关紧要;关键是编译器足够聪明,可以识别不需要强制转换。泛型在运行时消失,因此对T
的强制转换实际上只是对对象的强制转换(编译器实际上只需将其去掉),因此没有类强制转换异常
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();
您的代码在上一次失败,这是以前没有出现过的。启用所有编译器警告,您将看到答案:您的强制转换不是“安全的”。这意味着,它没有执行您认为它正在执行的操作。具体来说,泛型会受到类型擦除的影响,这意味着它们只是在编译时强制执行安全性的一种手段,而不是在运行时。在运行时,该方法不知道t
是Integer
。由于
的声明,编译器将(T)
转换为(对象)
。相关问题:另请参见我的相关问题。@KevinCruijssen编译器不关心或不知道字符串.valueOf
。字符串串联编译为一系列StringBuilder.append
s。如果显式调用toString
(可能是NPE),则追加,或者。在后一种情况下,String.valueOf
由StringBuilder
使用,但这是一个实现细节。@Michael-你说得对。我只是想指出,编译器不需要在编译后的代码中插入强制转换检查;我没有试图解释编译器是如何处理字符串连接的。不幸的是,在这个过程中,我产生了一个误导性的例子。我看看能不能重做那个部分答案。
...
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();
...
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;
...