@override在泛型擦除前的语义层校验源码签名是否完全匹配父类/接口方法,要求方法名、参数类型(含泛型形参)、个数、顺序、返回类型严格一致,桥接方法对其校验过程完全透明且无影响。

@Override 注解在类型擦除与桥接方法共存的场景下,依然严格校验“源码层面是否构成合法重写”——它不看字节码,只认编译器视角下的签名匹配;桥接方法是编译器自动生成的补救机制,对 @Override 的校验过程完全透明、不参与、也不影响其判断。
类型擦除本身不破坏 @Override 的校验逻辑
Java 编译器执行 @Override 检查时,是在泛型擦除前的语义层进行的。它比对的是你写的源码中方法名、参数类型(含泛型形参)、参数个数、顺序、返回类型(支持协变)是否与父类/接口中某个非 private/static/final 方法完全一致。
例如:
- 父类定义:
<t extends user> T load(T id)</t> - 子类若写成:
public User load(User id)→ 缺少泛型形参声明 → 编译报错 “method does not override” - 正确写法必须是:
<t extends user> public T load(T id)</t>
此时即使擦除后两者都变成 User load(User),@Override 也只关心你是否显式写出匹配的泛型结构——这是编译期契约,不是运行时推断。
桥接方法是编译器的“幕后工作”,与 @Override 无关
当泛型继承导致签名擦除后不一致(如父类 Box<string></string> 的 set(String) 与子类 Box<object></object> 的 set(Object)),编译器会自动生成一个桥接方法(bridge method),比如 set(Object),用来转发调用,确保多态行为正确。
但注意:
- @Override 注解只能加在你手写的那个“原始重写方法”上,不能加在桥接方法上(桥接方法由编译器生成,不可见、不可编辑)
- 你加了 @Override,编译器就只检查你写的那个方法是否满足重写规则;桥接方法是否存在、是否被生成,完全不影响这个判断
- 如果手写方法本身不满足重写条件,哪怕桥接方法能跑通,@Override 仍会报错
常见翻车点:泛型擦除导致“看似重写,实则无效”
以下情形看似合理,但加 @Override 会直接失败:
- 父类方法:
void process(List<string> list)</string> - 子类尝试重写:
@Override void process(List<integer> list)</integer>→ 报错!擦除后都是process(List list),但编译器禁止这种“伪重写”,因泛型参数不同不构成合法重写 - 父类是泛型类
Base<t></t>,子类继承为Child extends Base<string></string>,然后想重写Base<t>.handle(T)</t>,却写成handle(String)而没带<t></t>前缀 → 编译失败
这类错误不是桥接方法能掩盖的——@Override 正是帮你提前暴露它们。
验证技巧:用 javap 看 descriptor,理解真实契约
当你不确定是否真能重写,可借助工具确认 JVM 层面的方法签名是否对齐:
- 编译后执行:
javap -s ParentClass和javap -s ChildClass - 对比关键方法的 descriptor(如
(Ljava/util/List;)V),而非源码表面 - @Override 成功的前提,是子类方法的 descriptor 在擦除后,必须与父类对应方法的 descriptor 完全一致(且该方法可被重写)
这解释了为什么泛型边界(<t extends number></t>)、通配符(List extends CharSequence>)不影响重写判断——它们不出现在 descriptor 中,仅用于编译期检查。










