一、什么是桥接方法

在介绍桥接方法之前,我先介绍下泛型。在 JDK1.5 中引入了泛型,泛型类型是基于原始类型、类型擦除原理进行实现的。

原始类型
Java总是会自动的为泛型类型提供一个相应的原始类型。所谓原始类型就是是指泛型的第一个限定类型(从左向右),无限定类型泛型的原始类型默认为Object。

类型擦除
Java中泛型的实现原理是类型擦除(type erasure)。类型擦除是在编译器进行代码编译这个阶段进行的,在编译的时候泛型的类型参数会被原始类型(raw type)所替代。

进入正题,给出桥接方法的定义。桥接方法是在父类、子类的继承场景中出现的。父类是泛型类,且在该类中存在泛型方法。子类继承父类,并实现泛型方法。如果在子类实现中不包含父类经过类型擦除后生成的原始类型方法,则编译器会自动将该原始类型方法添加到子类中。这个被添加的原始类型方法我们称之为桥接方法。

二、桥接示例

1、定义泛型基类 Base

public interface Base<T> {/**** @param t*/void process(T t);
}

通过 javap -p Base.class 命令查看编译后的类信息,可以发现父类没有增加方法。

 javap -p Base.class
Compiled from "Base.java"
public interface org.learn.method.Base<T> {public abstract void process(T);
}

2、定义具体实现类 ConcreteString

public class ConcreteString implements Base<String> {@Overridepublic void process(String val) {System.out.println("i am a string.");}
}

通过 javap -p ConcreteString.class 命令查看编译后的类信息,我们可以发现,在 ConcreteString 类中多了一个方法 public void process(java.lang.Object); ,它就是我们本文要讨论的桥接方法。

zhibo@zhibo-mac method % javap -p ConcreteString.class
Compiled from "ConcreteString.java"
public class org.learn.method.ConcreteString implements org.learn.method.Base<java.lang.String> {public org.learn.method.ConcreteString();public void process(java.lang.String);public void process(java.lang.Object);
}

3、定义扩展类 ConcreteStringExt

public class ConcreteStringExt extends ConcreteString{public void init(){System.out.println("i am ConcreteStringExt");}
}

通过 javap -p ConcreteStringExt.class命令查看编译后的类信息,我们可以发现,在 ConcreteStringExt 类中并没有生成桥接方法。

javap -p ConcreteStringExt.class
Compiled from "ConcreteStringExt.java"
public class org.learn.method.ConcreteStringExt extends org.learn.method.ConcreteString {public org.learn.method.ConcreteStringExt();public void init();
}

4、在 ConcreteStringExt 在中重写 public void process(String val) 方法:

public class ConcreteStringExt extends ConcreteString{@Overridepublic void process(String val) {System.out.println("i am a string.");}public void init(){System.out.println("i am ConcreteStringExt");}
}

通过 javap -p ConcreteStringExt.class命令查看编译后的类信息,我们可以发现,此时在 ConcreteStringExt 类中生成了桥接方法。

javap -p ConcreteStringExt.class
Compiled from "ConcreteStringExt.java"
public class org.learn.method.ConcreteStringExt extends org.learn.method.ConcreteString {public org.learn.method.ConcreteStringExt();public void process(java.lang.String);public void init();public void process(java.lang.Object);
}

因此可以推测,桥接方法是伴随泛型方法而生的,在继承关系中,如果某个子类覆盖了泛型方法,则编译器会在该子类自动生成桥接方法。

三、为什么要生成桥接方法

解释一:
Base 类经过类型擦除转换为原始类型后,会生成 public void process(java.lang.Object); 方法,如果子类不实现该方法,则不满足 Java 接口语义。我认为这种解释有些牵强,因为在 java 语言规范中,是不允许在一个类中定义 public void process(String val)public void process(java.lang.Object); 这样的两个方法的。

解释二:
所有的 Base 类都定义了相同的 process方法,Base 类经过类型擦除转换为原始类型后,会生成 public void process(java.lang.Object); 方法,在子类定义了泛型的具体类型,导致子类中的 process 方法变化为 public void process(String val),此时父类的 public void process(java.lang.Object); 是没有被实现的,为了解决这个问题,Java 编译器通过桥接的方式实现了 public void process(java.lang.Object); 方法。 这样就保证了 Base 类的子类具有相同的一致的方法 public void process(java.lang.Object); 。在访问泛型对象时,通过父类方法 process 进行统一调用,而不需要关注子类的具体实现。

四、方法桥接了什么

通过下图可以清晰的看到, public void process(java.lang.Object); 经过类型转换后最终调用了 public void process(String val)

看到这里,就可以理解第二章中观察到的现象了。桥接方法是伴随泛型方法的实现而生,因此要把泛型方法的实现桥接到原型方法中。

“因此可以推测,桥接方法是伴随泛型方法而生的,在继承关系中,如果某个子类覆盖了泛型方法,则编译器会在该子类自动生成桥接方法。”

由此也可以推测出,桥接方法的生成与父类是接口、具体类、抽象类没有关系,只有父类的泛型方法的实现类有关系,在哪个类中实现泛型方法,就会生成对应的桥接方法。

五、如何判断桥接方法

通过 method.isBridge() 可以判定是否为桥接方法。

public class MainInteger {public static void main(String[] args) {try {ConcreteInteger concreteInteger = new ConcreteInteger();Method method = ConcreteInteger.class.getMethod("process", Integer.class);method.invoke(concreteInteger, 1);System.out.println(method.isBridge());method = ConcreteInteger.class.getMethod("process", Object.class);method.invoke(concreteInteger, 1);System.out.println(method.isBridge());} catch (Exception e) {e.printStackTrace();}}
}

六、使用场景

在 Mybatis中, MapperAnnotationBuilder 类的 parse 方进行了桥接方法的判定。在进行方法的反射访问时,我们也需要考虑桥接方法是否处理,比如在日志拦截器中,如果不排除桥接方法,在调用桥接方法时就会打印两份日志。

文章内容仅代表个人观点,如有不正之处,欢迎批评指正,谢谢大家。

Java编程:浅析泛型类型中的桥接方法相关推荐

  1. Java编程:Java的反射机制中的 getComponentType() 方法

    转载自  Java编程:Java的反射机制中的 getComponentType() 方法 Java 中所有的类都继承自 Object,数组本身也是一个 Class,如果我们能够得到数据的 Class ...

  2. 类的包访问权限:《Java编程思想》中一段话的困惑

    类的包访问权限:<Java编程思想>中一段话的困惑 在<java编程思想第三版>(陈昊鹏 饶若楠等译)的第五章隐藏具体实现中,5.4节的最后一段话是: "正如前面所提 ...

  3. java执行jar中的main_浅谈java 执行jar包中的main方法

    浅谈java 执行jar包中的main方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar 执行后总是运行指定的主方法,如果 jar 中有多个 ...

  4. 深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)

    一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程:所谓的串行编程就是单线程的作用下,按顺序执行.(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程:充分利用多核cpu的 ...

  5. Java系列:关于Java中的桥接方法

    这两天在看<Java核心技术 卷1>的泛型相关章节,其中说到了在泛型子类中override父类的泛型方法时,编译器会自动生成一个桥接方法,这块有点看不明白. 书上的例子代码如下: publ ...

  6. Java 底层知识:什么是 “桥接方法” ?

    作者 | 小志 来源 | 程序员小灰 导语 笔者在最近的日常工作中,因业务需要,研究 Java 字节码层面的知识.具体是,需要根据类字节码,获取特定方法名的方法入参,此方法名在源码中只有一个.但是在实 ...

  7. Java编程之反射中的注解详解

    "注解"这个词,可谓是在Java编程中出镜率比较高,而且也是一个老生常谈的话题.我们之前在聊Spring相关的东西时,注解是无处不在,之前我们简单的聊过一些"注解&quo ...

  8. 【java】浅析JDK中ServiceLoader的源码

    1.概述 转载:浅析JDK中ServiceLoader的源码 上一篇文章:深入探讨 Java 类加载器 2.ServiceLoader的使用 这里先列举一个经典的例子,MySQL的Java驱动就是通过 ...

  9. 关于学习java函数式接口Function中的apply方法的一些感悟

    起因是这样的,学习函数式编程的时候学到了Function接口,对于其中的apply方法感到不解,下面贴上我的不解代码 在这里插public class Function接口 {public stati ...

最新文章

  1. SCons — 程序构建工具
  2. WPF中用于嵌入其他进程窗口的自定义控件(AppContainer)
  3. 职称不用计算机考试吗,中级职称计算机考试需要什么条件可不用考试
  4. 如果你扯了团队后腿,你应该内疚
  5. const 一级指针的启示
  6. ICCV 2019 | 首个镜子分割网络问世,大连理工、鹏城实验室、香港城大出品
  7. 108扫货节完美收官 在线交易额破千万
  8. Excel 2010 VBA 入门 126 批量设置控件属性
  9. steam怎么设置邮箱令牌_【教程】电脑端steam动态令牌设置教程
  10. 计算机组装流程是什么,组装电脑的步骤
  11. OpenCV判断图片是否是黑白图片
  12. Python GUI工具——取色器
  13. 集成电路模拟版图入门——转行版图基础学习笔记(一)
  14. 张曦予巴黎时装周儿童单元T台走秀演绎童真风采
  15. 【大话设计模式-2】UML 类图的绘制(源码案例分析)
  16. 进阶C语言------>字符函数和字符串函数------strcmp函数
  17. 「重磅」2020下半年中国移动互联网大报告
  18. Linux离线安装Kafka(超级精简亲测安装)
  19. linux代码怎么运行gedit,Linux中gedit命令起什么作用呢?
  20. java开发框架emp入门教程,实战解析

热门文章

  1. 蓝桥杯单片机比赛学习:5、中断系统之外部中断的基本原理
  2. EM现象出现的原因及解决办法
  3. 结构化整理Unity资源文件夹
  4. VP-Expert的下载安装以及如何运行
  5. 使用ANTLR和Go实现DSL入门
  6. 大专程序员面试了25家公司,总结出来的痛苦经验!
  7. 如何安装Win10与Ubuntu16.04.5LTS
  8. IBus Pinyin 导入Sougou词库
  9. 冒号课堂§10.2:抽象类型
  10. 打印数字矩形 (python)