桥接方法是编译器为解决泛型擦除导致的签名不匹配而自动生成的public转发方法,用于维持JVM多态语义,可通过method.isBridge()识别,不可删除否则引发运行时错误。

桥接方法是什么:编译器自动生成的“补丁”
Java泛型擦除后,子类重写父类泛型方法时,字节码层面会出现签名不匹配——JVM认不出这是重写。桥接方法就是编译器悄悄生成的转发方法,用来维持多态语义。它不是你写的,但会影响反射、代理、序列化等场景。
常见错误现象:Method.isBridge() 返回 true 却被误当成业务方法;用 getDeclaredMethods() 遍历时多出一个看似重复的方法;Spring AOP 代理失败,报 IllegalArgumentException: object is not an instance of declaring class。
- 桥接方法永远是
public,即使原方法是protected - 它不带注解(
@Override是源码级,桥接方法没这个标记) - 调用栈里不会出现它——它只是跳板,最终仍调用你的实际方法
怎么识别桥接方法:看 isBridge() 和签名差异
最可靠的方式是用反射判断:method.isBridge()。但要注意:它只对方法有效,字段/构造器没有桥接概念。
典型场景:父类定义 <t> T get()</t>,子类实现 String get()。编译后,子类会多出一个桥接方法:
立即学习“Java免费学习笔记(深入)”;
public Object get() { return this.get(); }
这个方法就是桥接方法——签名擦除成 Object,内部调用你写的 String get()。
- 不要用
getName()或参数类型列表来人工判断是否桥接,容易漏(比如协变返回类型、泛型通配符场景) -
getGenericReturnType()在桥接方法上返回Object,在真实方法上才返回String或带泛型信息的Type - 使用
Method.getDeclaringClass()可区分:桥接方法声明类是子类,但它的逻辑归属仍是父类抽象契约
为什么不能删掉桥接方法:JVM 多态校验依赖它
擦除后,子类方法签名变成 String get(),而父类接口/父类方法签名是 Object get()。JVM 方法表(vtable)要求重写方法签名必须一致(或满足协变),否则运行时调用会失败。
桥接方法让 JVM 看到一个“符合擦除后签名”的方法,从而把多态分发正确路由过去。删掉它(比如 ASM 字节码操作时误过滤 isBridge() 方法),会导致:
- 向上转型后调用
get()报AbstractMethodError - 使用
invoke()反射调用时抛IllegalAccessException(因为桥接方法才是 public 入口) - Lombok 的
@SneakyThrows或@Delegate在泛型类中可能生成异常桥接逻辑
泛型擦除 + 桥接方法的坑:反射遍历和代理最常栽跟头
很多工具默认忽略桥接方法,但有些场景必须显式处理——尤其是你手动做反射调度、写通用 DAO、或实现 InvocationHandler 时。
例如 Spring 的 ReflectionUtils.doWithLocalMethods() 默认跳过桥接方法;但如果你用 getMethods() 获取所有可访问方法,桥接方法就会混进来。
- 代理目标对象时,
Proxy.newProxyInstance()不关心桥接,但 CGLIB 的Enhancer如果没设setUseCache(false),可能因桥接方法签名冲突生成失败类 - 用
MethodHandle.lookup().findSpecial()时,传入桥接方法会失败——必须传真实方法 - Gson / Jackson 序列化泛型字段时一般不受影响,但自定义
InstanceCreator或JsonDeserializer若依赖反射获取 setter,需先过滤isBridge()
桥接方法本身不可见、不可继承、也不该被业务代码直接调用——但它就在那儿,安静地维系着擦除世界里的多态契约。漏掉它,往往不是编译不过,而是运行时某次转型后突然断掉。








