这个问题已经在这里有了答案 :
为什么对泛型的这种使用不会引发运行时或编译时异常? (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。但是,当我尝试这样做时,它会按预期抛出异常。
+
System.out.println
toString
String sss = (Test.<Integer>abc(2)).toString();
它不会抛出,ClassCastException因为所有通用类型信息都已从编译后的代码中剥离(此过程称为类型擦除)。基本上,任何类型参数都由代替Object。这就是第一个版本起作用的原因。这也是代码完全编译的原因。如果您要求编译器通过-Xlint:unchecked标记警告未经检查的操作或不安全的操作,则会在的return语句中收到有关未经检查的强制转换的警告abc()。
ClassCastException
Object
-Xlint:unchecked
return
abc()
带有以下语句:
故事有点不同。当type参数T替换为时Object,调用代码将转换为字节代码,该字节代码将结果显式转换为Integer。就像代码是使用带有签名的方法编写的,static Object abc(Object)并且语句是这样编写的:
T
Integer
static Object abc(Object)
String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();
也就是说,内部abc()类型转换不仅会由于类型擦除而消失,而且编译器还会在调用代码中插入新的类型转换。该强制类型转换ClassCastException在运行时生成a ,因为从返回的对象abc()是String,而不是Integer。
String
注意声明
String ss = "" + (Test.<Integer>abc(2));
不需要强制转换,因为编译器只是将返回的对象提供给对象abc()的字符串连接操作。(有关此操作的详细信息随Java编译器的不同而不同,但这是对StringBuilderappend方法的调用,或者,从Java 9开始,是对StringConcatFactory。创建的方法的调用。)这里的细节无关紧要;关键是编译器足够聪明,可以识别出不需要强制转换。
StringBuilder
StringConcatFactory