桥接方法是java编译器为解决泛型擦除导致的多态失效而自动生成的合成方法,标记为acc_bridge和acc_synthetic,仅含类型转换与转发逻辑,确保jvm正确分派方法调用。

怎么看字节码里有没有桥接方法
直接用 javap -c -v ClassName.class,重点看方法表里带 bridge 和 synthetic 标志的方法。这类方法不会出现在源码中,但 JVM 会把它当作重写父类方法的有效候选——这是多态能成立的关键前提。
常见错误现象:你写了 @Override 却发现运行时调用的不是子类方法,或者反射获取 getDeclaredMethods() 时漏掉了实际被调用的那个入口,往往就是忽略了桥接方法的存在。
使用场景集中在泛型继承链中,比如子类继承 ArrayList<string></string> 并重写 add(String),或实现 Comparable<t></t> 接口时重写 compareTo(T)。
桥接方法签名为什么和父类擦除后一致
因为 JVM 方法分派只认签名(方法名 + 参数类型 + 返回类型),不认泛型。父类泛型方法擦除后变成 Object get(),子类若只写 String get(),签名就不匹配,JVM 认为这不是重写,而是重载——多态就断了。
所以编译器必须补一个桥接方法,满足三点:
- 方法名、参数类型、返回类型与父类擦除后完全一致(如
public Object get()) - 内部只是强制转型后转发给子类真实方法(如
return (String) this.get();) - 标记为
bridge和synthetic,让 JVM 特殊对待,视作有效重写
javap 输出里怎么区分桥接方法和普通方法
打开 javap -v 输出,每个方法块末尾有 flags: 行。只要看到同时含 bridge 和 synthetic 的,就是桥接方法。例如:
public java.lang.Object get(); flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
注意:它没有源码行号(LineNumberTable 缺失)、没有局部变量表(LocalVariableTable 为空),这些是人工写的代码不会有的特征。
容易踩的坑:
- 用
getMethods()反射获取所有 public 方法时,桥接方法会被包含;但用getDeclaredMethods()时,它也会出现——别误以为是自己写的 - 某些 IDE 的“Go to Implementation”可能跳转到桥接方法而非真实逻辑,需手动点进真实方法体确认
- 性能上几乎无影响:桥接方法就是一条转型 + 调用指令,JIT 很快会内联
为什么不能靠反射绕过桥接方法直接调用子类方法
因为 JVM 的 invokevirtual 指令在运行时查虚方法表(vtable),只认签名匹配项。子类真实方法(如 String get())签名和父类擦除后(Object get())不一致,根本进不了 vtable 的同一槽位。
换句话说:不是“不想调”,是 JVM 根本找不到入口。桥接方法才是那个被放进 vtable、承接父类引用调用的“合法代理”。
这也解释了为什么 list instanceof ArrayList<string></string> 编译报错——类型信息已擦除,JVM 运行时连 ArrayList<string></string> 这个类型都不存在,更别说把它塞进 vtable 了。
真正容易被忽略的点在于:桥接方法不是“辅助逻辑”,它是 JVM 多态机制在泛型语境下的必要适配层。没它,泛型继承就退化成静态绑定,整个面向对象的基石就松动了。











