桥接方法

栏目: Java · 发布时间: 7年前

内容简介:Java语言的重写与JVM的重写并不一致,当在Java语言中为重写而在JVM中为非重写,编译器会通过生成

Java语言的重写与JVM的重写并不一致,当在 Java 语言中为重写而在JVM中为非重写,编译器会通过生成 桥接方法 来实现Java中的重写语义

桥接方法 – 返回类型

Java代码

@Slf4j
public class Father {
    public Number work() {
        return 1.0;
    }

    public static void main(String[] args) {
        Father father = new Son();
        // 实际调用的是桥接方法
        Number work = father.work();
        log.info("{}", work);
    }
}

class Son extends Father {
    @Override
    public Double work() {
        return 2.0;
    }
}

字节码

$ javap -v -c Son
public java.lang.Double work();
  descriptor: ()Ljava/lang/Double;
  flags: ACC_PUBLIC
  Code:
    stack=2, locals=1, args_size=1
       0: ldc2_w        #2                  // double 2.0d
       3: invokestatic  #4                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       6: areturn
    LineNumberTable:
      line 21: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       7     0  this   Lme/zhongmingmao/basic/bridge/return_type/Son;

// 桥接办法
public java.lang.Number work();
  descriptor: ()Ljava/lang/Number;
  // ACC_BRIDGE:桥接方法;ACC_SYNTHETIC:编译器自动生成
  flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  Code:
    stack=1, locals=1, args_size=1
       0: aload_0
       // 调用Son本身重写的方法
       1: invokevirtual #5                  // Method work:()Ljava/lang/Double;
       4: areturn
    LineNumberTable:
      line 18: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Lme/zhongmingmao/basic/bridge/return_type/Son;
  1. 如果没有桥接办法,对于Java语言是重写的,但对于JVM来说却不是重写的
    • 只有当两个方法的 参数类型返回类型 一致,JVM才会判定为重写
    • Java编译器会在Son的字节码中 自动生成一个桥接方法 来保证重写语义

翻译桥接方法

public Number work() {
    return this.work();
}

调用桥接办法

  1. 编译器通过插入 桥接办法 来保证重写的语义
  2. JVM通过 方法描述符 (参数类型+返回类型)定位到具体的方法
Father father = new Son();
// 实际调用的是桥接方法
Number work = father.work();
log.info("{}", work);

桥接方法 – 泛型

Java代码

public interface Father {
    void work();

    static void main(String[] args) {
        Job job = new Doctor();
        // 调用实际的方法
        job.work(new Son());
        // 调用桥接方法,有checkcast指令,抛出ClassCastException
        job.work(new Daughter());
    }
}

class Son implements Father {
    @Override
    public void work() {
    }
}

class Daughter implements Father {
    @Override
    public void work() {
    }
}

abstract class Job<T extends Father> {
    protected void work(T father) {
        father.work();
    }
}

class Doctor extends Job<Son> {
    @Override
    public void work(Son son) {
        super.work(son);
    }
}

class Nurse extends Job<Daughter> {
    @Override
    public void work(Daughter daughter) {
        super.work(daughter);
    }
}

字节码

$ javap -v -c Job
protected void work(T);
  // Java是伪泛型,会进行类型擦除
  // 因此泛型T被换成Father,方法签名为protected void work(Father father)
  descriptor: (Lme/zhongmingmao/basic/bridge/generic/Father;)V
  flags: ACC_PROTECTED
  Code:
    stack=1, locals=2, args_size=2
       0: aload_1
       // 调用接口方法
       1: invokeinterface #2,  1            // InterfaceMethod me/zhongmingmao/basic/bridge/generic/Father.work:()V
       6: return
    LineNumberTable:
      line 27: 0
      line 28: 6
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       7     0  this   Lme/zhongmingmao/basic/bridge/generic/Job;
          0       7     1 father   Lme/zhongmingmao/basic/bridge/generic/Father;
    LocalVariableTypeTable:
      Start  Length  Slot  Name   Signature
          0       7     0  this   Lme/zhongmingmao/basic/bridge/generic/Job<TT;>;
          0       7     1 father   TT;
  Signature: #20                          // (TT;)V
$ javap -v -c Doctor
public void work(me.zhongmingmao.basic.bridge.generic.Son);
  descriptor: (Lme/zhongmingmao/basic/bridge/generic/Son;)V
  flags: ACC_PUBLIC
  Code:
    stack=2, locals=2, args_size=2
       0: aload_0
       1: aload_1
       2: invokespecial #2                  // Method me/zhongmingmao/basic/bridge/generic/Job.work:(Lme/zhongmingmao/basic/bridge/generic/Father;)V
       5: return
    LineNumberTable:
      line 34: 0
      line 35: 5
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       6     0  this   Lme/zhongmingmao/basic/bridge/generic/Doctor;
          0       6     1   son   Lme/zhongmingmao/basic/bridge/generic/Son;

// 桥接方法
public void work(me.zhongmingmao.basic.bridge.generic.Father);
  descriptor: (Lme/zhongmingmao/basic/bridge/generic/Father;)V
  flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  Code:
    stack=2, locals=2, args_size=2
       0: aload_0
       1: aload_1
       // 类型校验,必须为Son类型
       2: checkcast     #3                  // class me/zhongmingmao/basic/bridge/generic/Son
       // 调用Doctor本身重写的方法(非私有实例方法)
       5: invokevirtual #4                  // Method work:(Lme/zhongmingmao/basic/bridge/generic/Son;)V
       8: return
    LineNumberTable:
      line 31: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       9     0  this   Lme/zhongmingmao/basic/bridge/generic/Doctor;

翻译桥接办法

public void work(Father father){
    // 强制类型转换
    super.work((Son) father);
}

调用桥接办法

Job job = new Doctor();
// 调用实际的方法
job.work(new Son());
// 调用桥接方法,有checkcast指令,抛出ClassCastException
job.work(new Daughter());

转载请注明出处:http://zhongmingmao.me/2018/12/18/jvm-basic-bridge-method/

访问原文「桥接方法」获取最佳阅读体验并参与讨论


以上所述就是小编给大家介绍的《桥接方法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Mining

Data Mining

Jiawei Han、Micheline Kamber、Jian Pei / Morgan Kaufmann / 2011-7-6 / USD 74.95

The increasing volume of data in modern business and science calls for more complex and sophisticated tools. Although advances in data mining technology have made extensive data collection much easier......一起来看看 《Data Mining》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具