为什么违背泛型类型边界的代码可正常编译且无编译报错
泛型方法返回值可赋值给未实现对应接口的String类型的编译原因
你观察到的现象是符合Java编译器的泛型处理逻辑的,核心原因和泛型的类型推导、类型擦除规则有关:
核心逻辑梳理
- 你的泛型方法签名为
<Z extends I> Z get(),类型参数Z的唯一约束是必须实现I接口,且该方法没有入参可以给编译器提供额外的Z类型约束,因此Z的实际类型完全由赋值的目标类型推导得出。 - 当你写
String b = get()时,编译器会推导Z的类型为同时满足实现I接口且是String的子类型的交叉类型。虽然String是final类,不存在同时满足两个条件的实际类型,但Java编译器在泛型推导阶段不会校验final类的子类型可能性,仅会做基础的类型兼容性判断。 - 泛型经过类型擦除后,
get()方法的实际返回值类型为上界I(接口类型),编译器会在赋值处自动插入隐式强制转型,等效于你手动写了String b = (String) get();。由于源类型是接口,编译器认为存在某个实现了I接口的类同时继承自String的可能性,因此不会在编译期报错,只会把类型校验延迟到运行时执行,运行时发现类型不匹配就会抛出ClassCastException。
补充验证:如果你把
I从接口改成普通类,再执行相同的赋值操作就会直接编译失败。因为擦除后的返回值是I类类型,编译器可以直接判定I和String没有继承关系,不可能转型成功,因此提前抛出编译错误。
测试代码格式化后如下
import static org.junit.Assert.assertNotNull; import org.junit.Test; public class TestGenerics { interface I{} class A implements I{} private <Z extends I> Z get(){ return (Z) new A(); } @Test public void test() { A a1 = get(); assertNotNull(a1); String b = get(); // 这里编译可以通过,运行时才会报错 } }
内容的提问来源于stack exchange,提问作者Alessandro Carraro
相关产品推荐
相关产品推荐

