因为泛型擦除导致接口与子类方法签名不一致,编译器自动生成ACC_BRIDGE|ACC_SYNTHETIC桥接方法,其参数和返回类型按擦除后签名定义,内部仅类型转换并转发至真实实现方法。

为什么接口继承泛型方法后,invokevirtual 还能调到子类实现
因为 JVM 多态分派只认方法签名(名称 + 描述符),而泛型擦除后父接口和子类实际方法的描述符可能不一致,编译器必须补一个桥接方法来“对齐签名”。没有它,invokevirtual 会直接失败或调错方法。
javap -v 看到的 ACC_BRIDGE | ACC_SYNTHETIC 方法就是关键
桥接方法不是你写的,是编译器生成的合成方法,字节码里带这两个标记。它参数和返回类型都按父接口擦除后的签名定义,内部只做类型转换 + 转发:
public java.lang.Object getData();
descriptor: ()Ljava/lang/Object;
flags: (0x0041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #23 // Method getData:()Ljava/lang/String;
4: areturn
- 这个
getData()返回Object,是为了匹配接口擦除后的签名 - 它内部调用的是你写的、真正返回
String的那个方法 - 如果你用反射遍历
getDeclaredMethods(),它也会出现——但isBridge()返回true
接口继承中桥接方法的触发条件很具体
不是所有泛型接口实现都会生成桥接方法,只在以下情况同时满足时才出现:
- 父接口声明了泛型方法,如
<T> T get(); - 子类/实现类将该方法具体化,比如写成
String get() { ... } - 擦除后,父接口方法签名变成
Object get(),而子类方法是String get()—— JVM 认为这是两个不同方法,无法覆盖 - 编译器于是插入一个
Object get()的桥接方法,转发到String get()
如果子类也写成 Object get(),或者用原始类型实现,桥接方法就不会生成。
立即学习“Java免费学习笔记(深入)”;
调试时看到“多了一个奇怪的方法”,别急着删
IDE 或 JFR 采样里偶尔会看到名字正常但签名奇怪的方法,比如 set(java.lang.Object) 和你写的 set(java.lang.String) 并存——大概率就是桥接方法。
- 它不可被 Java 源码直接调用,也不参与重载解析
- 但它对
invokeinterface分派至关重要:JVM 查表时靠它把接口调用导向正确子类实现 - 混淆时若没配置保留
bridge方法,可能导致运行时AbstractMethodError
最易忽略的是:桥接方法的异常栈帧里不会显示源码行号,且 StackTraceElement.getFileName() 可能返回 null 或 Unknown Source —— 这是它作为合成方法的固有表现,不是环境问题。


















