桥接方法

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

内容简介: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/

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


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

查看所有标签

猜你喜欢:

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

运营实战指南

运营实战指南

韩利 / 电子工业出版社 / 2016-9-1 / 49

《运营实战指南》架构清晰,前8章主要通过故事形式深入浅出理解运营,将运营基础知识和概念融入到故事中。第9章讲解运营核心方法论,从目标、关键驱动元素、试错调优、高效运行4部分来完整讲解一个运营项目从0到1的过程。第10章、11章、12章深入讲解了运营人拿业绩最核心的知识点:用户、内容和文案。其中数据分析、活动运营等内容以案例形式穿插在各个章节中。最后两章,主谈运营人在日常生活中如何历练以及一个运营人......一起来看看 《运营实战指南》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试