bigywater.tech
Java · 222

桥接方法:泛型重写背后的隐藏方法

泛型擦除之后,Comparable 的 compareTo(Box) 如何覆盖 compareTo(Object)?答案是 javac 偷偷补的桥接方法。这篇用 javap 和反射看清它们的字节码、标志位、注解以及在异常栈里的表现。

3 → 6
Box 源码里的 3 个方法在 class 文件里变成 6 个
0x1041
桥接方法的访问标志:ACC_PUBLIC|ACC_BRIDGE|ACC_SYNTHETIC
第 4 行
桥接方法在栈里显示的行号(类声明所在行)

javap:桥接方法的字节码

实测 · 本机执行输出
  public lib.Box(int)                                  flags: (0x0001) ACC_PUBLIC
  public int compareTo(lib.Box)                        flags: (0x0001) ACC_PUBLIC
  public java.lang.String get()                        flags: (0x0001) ACC_PUBLIC
  public lib.Box clone()                               flags: (0x0001) ACC_PUBLIC
  public int compareTo(java.lang.Object)               flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  public java.lang.Object get()                        flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  桥接方法 compareTo(Object) 的字节码:
    Code:
    0: aload_0
    1: aload_1
    2: checkcast     #8                  // class lib/Box
    5: invokevirtual #37                 // Method compareTo:(Llib/Box;)I
    8: ireturn
  Visible 里的方法(visibility bridge):
    #7 = Methodref          #2.#8          // lib/Hidden.hello:()V
    #8 = NameAndType        #9:#6          // hello:()V
    #9 = Utf8               hello
    public void hello();
    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    1: invokespecial #7                  // Method lib/Hidden.hello:()V

反射:isBridge 与注解

实测 · 本机执行输出
  clone[]                              返回 Object             [bridge synthetic public volatile] 注解:[]
  clone[]                              返回 Box                [public] 注解:[]
  compareTo[Object]                    返回 int                [bridge synthetic public volatile] 注解:[Tag]
  compareTo[Box]                       返回 int                [public] 注解:[Tag]
  get[]                                返回 Object             [bridge synthetic public volatile] 注解:[]
  get[]                                返回 String             [public] 注解:[]
  getDeclaredMethods() 共 6 个;其中桥接方法 3 个;源码里写了 3 个方法(compareTo、get、clone),另外 3 个都是 javac 生成的桥接方法
  getMethod("compareTo", Object.class) → bridge = true;getMethod("compareTo", Box.class) → bridge = false
  反射调用桥接版本 compareTo(Object) = -1;调用真实版本 = -1
  经桥接版本传入 String → InvocationTargetException,cause = ClassCastException: class java.lang.String cannot be cast to class lib.Box
  经真实版本传入 String → IllegalArgumentException: argument type mismatch
  Visible.getDeclaredMethods():hello [bridge synthetic public volatile],声明类 Visible
  Visible.class.getMethod("hello").getDeclaringClass() = Visible,bridge = true

运行时:异常栈与可见性桥接

实测 · 本机执行输出
  原始类型调用 compareTo("oops") → ClassCastException: class java.lang.String cannot be cast to class lib.Box
      栈帧 0: lib.Box.compareTo(Box.java:4)
      栈帧 1: Main.main(Main.java:20)
  堆污染:List<Box> 里被塞了一个 String,Collections.sort → ClassCastException: class lib.Box cannot be cast to class java.lang.String
      栈帧 0: java.lang.String.compareTo(String.java:142)
      栈帧 1: java.util.ComparableTimSort.binarySort(ComparableTimSort.java:262)
  String.class.getMethod("compareTo", Object.class).isBridge() = true(JDK 自己的 String 也有桥接方法)
  通过接口 Supplier<String> 调用 get():box7
  clone() 的返回类型是 Box,但 Object.clone() 的是 Object:Box 的 clone 桥接 + 真实共 2 个方法
hello from Hidden
hello from Hidden
  Visible 继承自包私有类 Hidden:v.hello() 与反射 m.invoke(v) 都成功(没有 IllegalAccessException)

分步演示

桥接方法
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 的 javac 与 javap
    • 没有验证:Kotlin、Scala 等其它语言编译器生成的桥接方法;JDK 8 之前注解是否被复制;桥接方法对 JIT 内联的影响(没有测);lambda 与方法引用背后的合成方法(另见 invokedynamic 相关的篇目)
    京ICP备2026067502号-1京公网安备11010502063487号