桥接方法是编译器为解决泛型擦除导致的重写失效而生成的合成方法,签名匹配擦除后的父类方法(如setdata(object)),内部进行类型检查并转发到子类具体实现。

泛型擦除后,JVM 看不到 T,只认具体类型(如 Object 或边界类)。当子类用具体类型重写泛型方法时,签名不匹配,编译器就悄悄补一个参数为 Object 的桥接方法——它不写在源码里,但真实存在于字节码中,且是多态能走通的关键。
为什么必须生成 Object 参数的桥接方法
父类泛型方法 void setData(T) 擦除后变成 void setData(Object);子类若写 void setData(String),JVM 认为这是两个独立方法,不构成重写。没有桥接方法,通过父类引用调用就会跳过子类逻辑,直接执行父类版本。
- 多态失效:
Node<string> n = new MyNode(); n.setData("hi");</string>表面调子类,实际走父类setData(Object) - 强转风险暴露:父类方法体若含
(T)data,传入非预期类型(如Integer)会在运行时抛ClassCastException - 桥接方法就是那个“补丁”:签名完全匹配
setData(Object),内部仅做(String)data转换 + 转发
用 javap -v 直接看到桥接方法的真身
运行 javap -v MyNode.class | grep -A 5 -B 5 Bridge,你会看到类似输出:
public void setData(java.lang.Object);
descriptor: (Ljava/lang/Object;)V
flags: (0x0041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #23 // class java/lang/String
5: invokevirtual #25 // Method setData:(Ljava/lang/String;)V
8: return
-
ACC_BRIDGE | ACC_SYNTHETIC是铁证:这是编译器生成的合成方法,不是你写的 -
descriptor: (Ljava/lang/Object;)V表明它接收Object,满足 JVM 对重写的签名要求 -
checkcast指令完成窄化转换,invokevirtual转发到你写的setData(String)
桥接方法在反射和调试中的表现
它会出现在 getDeclaredMethods() 结果里,但 method.isBridge() 返回 true 可以识别它。
- IDE 的 “Go to Implementation” 常跳转到你写的
setData(String),但实际运行时调用的是桥接方法 - Spring AOP、某些代理框架若未过滤桥接方法,可能重复拦截或出错
- 反射调用时若误选了桥接方法,传入
Object参数也能成功,但内部强转会失败——错误根源常被掩盖
什么情况下不会生成桥接方法
桥接方法不是无条件出现的,只在签名真正“错位”时才插入:
- 子类方法签名与擦除后父类签名一致:比如都声明为
setData(Object),就不需要桥接 - 接口方法使用原始类型实现:如
Processor接口的process(T),子类写成process(Object),也无需桥接 - 泛型接口的默认方法、静态方法、构造器,都不会触发桥接方法生成
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











