为何泛型原始类型对边界检查的忽略行为不一致?
ExampleExtended的get()返回Number而非Integer? 这其实是Java泛型擦除机制里原始类型处理规则的特定表现,咱们一步步拆解来理解:
核心规则:原始类型仅擦除当前类的泛型边界,父类的边界保留
当你使用一个泛型类的原始类型(比如ExampleExtended而非ExampleExtended<?>)时,Java编译器会遵循两个关键逻辑:
- 擦除当前类定义的泛型参数及其边界(也就是
ExampleExtended<C extends Integer>里的<C extends Integer>会被完全抹掉) - 父类的泛型擦除规则独立生效,父类的泛型边界会被保留为它的上限类型
结合你的代码逐点分析
先看你的类结构:
abstract class GenericExample<C extends Number> { public abstract C get(); } abstract class ExampleExtended<C extends Integer> extends GenericExample<C> { }
父类
GenericExample的擦除结果:
父类的泛型参数C有边界extends Number,泛型擦除后,这个参数会被替换为它的上限类型Number,所以父类的get()方法签名最终变成public abstract Number get();原始类型
ExampleExtended的处理逻辑:
当你声明ExampleExtended example2时,子类自己的<C extends Integer>边界被擦除了——但子类并没有重写get()方法(它是抽象类,直接继承了父类的get()),所以它继承的是父类已经擦除后的get()方法,返回类型自然就是Number。带通配符的
ExampleExtended<?>的区别:
当你用ExampleExtended<?> example1时,这不是原始类型,是带无界通配符的参数化类型。此时子类的泛型边界<C extends Integer>依然有效,通配符?的上限是Integer,所以get()方法的返回类型会被推断为Integer,这也符合你一开始的预期。
为什么不是返回Object?
你可能会疑惑:如果边界全被擦除,应该返回Object才对?但Java的泛型擦除规则里,父类的泛型边界不会因为子类使用原始类型而被二次擦除。父类本身的泛型擦除是独立完成的,它的方法签名已经固定为返回Number,子类作为原始类型时只是继承这个已擦除的方法,不会把父类的返回类型再降级到Object。
内容的提问来源于stack exchange,提问作者TomHardlyACT

