You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何泛型原始类型对边界检查的忽略行为不一致?

为什么原始类型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> {
}
  1. 父类GenericExample的擦除结果:
    父类的泛型参数C有边界extends Number,泛型擦除后,这个参数会被替换为它的上限类型Number,所以父类的get()方法签名最终变成public abstract Number get();

  2. 原始类型ExampleExtended的处理逻辑:
    当你声明ExampleExtended example2时,子类自己的<C extends Integer>边界被擦除了——但子类并没有重写get()方法(它是抽象类,直接继承了父类的get()),所以它继承的是父类已经擦除后的get()方法,返回类型自然就是Number。

  3. 带通配符的ExampleExtended<?>的区别:
    当你用ExampleExtended<?> example1时,这不是原始类型,是带无界通配符的参数化类型。此时子类的泛型边界<C extends Integer>依然有效,通配符?的上限是Integer,所以get()方法的返回类型会被推断为Integer,这也符合你一开始的预期。

为什么不是返回Object?

你可能会疑惑:如果边界全被擦除,应该返回Object才对?但Java的泛型擦除规则里,父类的泛型边界不会因为子类使用原始类型而被二次擦除。父类本身的泛型擦除是独立完成的,它的方法签名已经固定为返回Number,子类作为原始类型时只是继承这个已擦除的方法,不会把父类的返回类型再降级到Object。


内容的提问来源于stack exchange,提问作者TomHardlyACT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:02:47