Java9泛型方法引发ClassCastException的原因及编译疑问
先看你提供的代码:
package stackoverflow; public class Main { public static void main(String[] args) { String.valueOf(hoge()); } static <E> E hoge() { return (E) "hoge"; } }
运行后抛出的错误:
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.String cannot be cast to [C at stackoverflow.Main.main(Main.java:6)
咱们拆解两个核心问题来解释:
1. 为什么程序抛出ClassCastException,而非编译错误?
Java的泛型是基于类型擦除实现的——编译阶段泛型类型信息会被移除,编译器无法在编译时确定E的具体类型。你的hoge()方法是无界泛型方法,编译器只能做“未经检查的强制转换”,它会给你一个编译警告(如果开启了警告选项的话),但不会直接阻止编译。
到了运行时,JVM需要把hoge()返回的String对象转换成实际被推断出的E类型,而这个类型和String不兼容,所以就抛出了ClassCastException。
2. 为什么编译器选择String.valueOf(char[])而非String.valueOf(Object)重载?
这是Java方法重载解析的规则在起作用:编译器会优先匹配更具体的重载方法。
当你调用String.valueOf(hoge())时,编译器需要推断E的类型来匹配最合适的重载。String.valueOf(char[])是针对char[]的专用重载,比通用的String.valueOf(Object)更具体。编译器会自动推断E为char[](JVM内部表示为[C),这样就能匹配到这个更具体的方法。
但运行时hoge()实际返回的是String,强行转成char[]自然就触发了类型转换异常。如果想让编译器选择String.valueOf(Object),你可以显式指定泛型类型:
String.valueOf(Main.<Object>hoge());
这样E被明确指定为Object,就会匹配通用重载,运行时也不会报错,因为String本身就是Object的子类。
内容的提问来源于stack exchange,提问作者user4910279

