Java泛型奇怪地强制转换

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) {

我正在使用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) {
        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;
...