方法重载与泛型方法的默认返回类型异常问题
代码结构
SomeUtil类
public class SomeUtil { public static void doSmth(boolean b) { System.out.println("boolean"); } public static void doSmth(String b) { System.out.println("string"); } public static void doSmth(Object b) { System.out.println("object"); } }
SomeCl接口
public interface SomeCl { <T> T getAttribute(Object var1); }
SomeClImpl实现类
public class SomeClImpl implements SomeCl { @Override public <T> T getAttribute(Object var1) { Boolean o = Boolean.TRUE; return (T)o; } }
Demo测试类
public class Demo { public static void main(String[] args) { SomeCl someCl = new SomeClImpl(); SomeUtil.doSmth(someCl.getAttribute(Boolean.TRUE)); } }
运行异常
Exception in thread "main" java.lang.ClassCastException: class java.lang.Boolean cannot be cast to class java.lang.String (java.lang.Boolean and java.lang.String are in module java.base of loader 'bootstrap') at Demo2.main(Demo2.java:4)
疑问
为何JVM会尝试将Boolean类型转换为String类型,而非Object类型?
这是Java方法重载解析和泛型类型擦除共同作用的结果:
泛型类型擦除:
SomeCl.getAttribute方法的泛型<T>在编译期会被擦除为Object,但编译器会根据方法调用的上下文推断T的类型——这里是为了匹配SomeUtil.doSmth的重载方法,编译器会优先尝试匹配最具体的重载版本。重载方法的优先级匹配:Java解析重载方法时,遵循从具体到宽泛的优先级:
- 首先尝试匹配基本类型,但
getAttribute返回的是包装类Boolean,无法直接匹配doSmth(boolean); - 接着匹配具体引用类型:
String是比Object更具体的类型,编译器会推断T为String,以此尝试匹配doSmth(String)这个更精准的重载; - 只有当前面的匹配都不成立时,才会选择最宽泛的
doSmth(Object)。
- 首先尝试匹配基本类型,但
运行时类型转换失败:由于泛型擦除,
getAttribute实际返回的是Object类型的Boolean实例,但编译器为了匹配doSmth(String),会在字节码中插入String类型的强制转换逻辑,运行时该转换无法完成,因此抛出ClassCastException。
简单来说:编译器为了选择更具体的重载方法,推断泛型T为String,导致运行时尝试把实际的Boolean实例转成String,最终触发异常。
内容的提问来源于stack exchange,提问作者Maksym Kosenko

