BridgeMethod【桥接方法】

桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。可以通过Method.isBridge()方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC

从jvm规范中截取的两条记录,可以看到,ACC_BRIDGE表示方法是一个bridge method,而ACC_SYNTHETIC则表示该方法不在源码中展示。

Flag NameValueInterpretation
ACC_BRIDGE0x0040A bridge method, generated by the compiler.
ACC_SYNTHETIC0x1000Declared synthetic; not present in the source code.
什么是桥接方法呢

参照The Java™ Tutorials

When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, which is called a bridge method, as part of the type erasure process. You normally don’t need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.

简单翻译一下就是:当编译一个类(或接口)继承了一个泛型类(或实现了一个泛型接口),编译器可能需要创建一个合成方法(其实就是类型转换用的方法),用于泛型的类型安全处理。使用者不需要关心这个方法,但是从堆栈里看到这个方法可能会让人疑惑。

通过官网的例子具体看下什么是桥接方法

/**
 * 带泛型参数的父类
 */
public class Node<T> {
    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

/**
 * 使用泛型的子类
 */
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

因为Java的泛型会进行类型擦除,所以编译后会变成这样

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

可以看到Node.setData(T)变成了Node.setData(Object),缺失了对父类方法的重写,所以就变成了下面这样

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }
    
    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");

        super.setData(data);
    }
}

通过javap看下编译后的内容,可以看到MyNode类里多了一个public void setData(java.lang.Object)方法。

$ javap MyNode.class 
Compiled from "MyNode.java"
public class com.st.bytecode.MyNode extends com.st.bytecode.Node<java.lang.Integer> {
  public com.st.bytecode.MyNode(java.lang.Integer);
  public void setData(java.lang.Integer);
  public void setData(java.lang.Object);
}

$ javap -c MyNode.class 
Compiled from "MyNode.java"
public class com.st.bytecode.MyNode extends com.st.bytecode.Node<java.lang.Integer> {
  public com.st.bytecode.MyNode(java.lang.Integer);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #1                  // Method com/st/bytecode/Node."<init>":(Ljava/lang/Object;)V
       5: return

  public void setData(java.lang.Integer);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String MyNode.setData
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_0
       9: aload_1
      10: invokespecial #5                  // Method com/st/bytecode/Node.setData:(Ljava/lang/Object;)V
      13: return

  public void setData(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #6                  // class java/lang/Integer
       5: invokevirtual #7                  // Method setData:(Ljava/lang/Integer;)V
       8: return
}

为什么要生成bridge方法呢

简单来说, 编译器生成bridge method的目的就是为了和jdk1.5之前的字节码兼容。因为范型是在jdk1.5之后才引入的。在jdk1.5之前例如集合的操作都是没有范型支持的, 所以生成的字节码中参数都是用Object接收的, 所以也可以往集合中放入任意类型的对象, 集合类型的校验也被拖到运行期。

但是在jdk1.5之后引入了范型, 因此集合的内容校验被提前到了编译期, 但是为了兼容jdk1.5之前的版本java使用了范型擦除, 所以如果不生成桥接方法就和jdk1.5之前的字节码不兼容了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吊儿郎当当

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值