Java编程:浅析泛型类型中的桥接方法
一、什么是桥接方法
在介绍桥接方法之前,我先介绍下泛型。在 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编程:浅析泛型类型中的桥接方法相关推荐
- Java编程:Java的反射机制中的 getComponentType() 方法
转载自 Java编程:Java的反射机制中的 getComponentType() 方法 Java 中所有的类都继承自 Object,数组本身也是一个 Class,如果我们能够得到数据的 Class ...
- 类的包访问权限:《Java编程思想》中一段话的困惑
类的包访问权限:<Java编程思想>中一段话的困惑 在<java编程思想第三版>(陈昊鹏 饶若楠等译)的第五章隐藏具体实现中,5.4节的最后一段话是: "正如前面所提 ...
- java执行jar中的main_浅谈java 执行jar包中的main方法
浅谈java 执行jar包中的main方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar 执行后总是运行指定的主方法,如果 jar 中有多个 ...
- 深究并行编程Parallel类中的三大方法 (For、ForEach、Invoke)和几大编程模型(SPM、APM、EAP、TAP)
一. 并行编程 1. 区分串行编程和串行编程 ①. 串行编程:所谓的串行编程就是单线程的作用下,按顺序执行.(典型代表for循环 下面例子从1-100按顺序执行) ②. 并行编程:充分利用多核cpu的 ...
- Java系列:关于Java中的桥接方法
这两天在看<Java核心技术 卷1>的泛型相关章节,其中说到了在泛型子类中override父类的泛型方法时,编译器会自动生成一个桥接方法,这块有点看不明白. 书上的例子代码如下: publ ...
- Java 底层知识:什么是 “桥接方法” ?
作者 | 小志 来源 | 程序员小灰 导语 笔者在最近的日常工作中,因业务需要,研究 Java 字节码层面的知识.具体是,需要根据类字节码,获取特定方法名的方法入参,此方法名在源码中只有一个.但是在实 ...
- Java编程之反射中的注解详解
"注解"这个词,可谓是在Java编程中出镜率比较高,而且也是一个老生常谈的话题.我们之前在聊Spring相关的东西时,注解是无处不在,之前我们简单的聊过一些"注解&quo ...
- 【java】浅析JDK中ServiceLoader的源码
1.概述 转载:浅析JDK中ServiceLoader的源码 上一篇文章:深入探讨 Java 类加载器 2.ServiceLoader的使用 这里先列举一个经典的例子,MySQL的Java驱动就是通过 ...
- 关于学习java函数式接口Function中的apply方法的一些感悟
起因是这样的,学习函数式编程的时候学到了Function接口,对于其中的apply方法感到不解,下面贴上我的不解代码 在这里插public class Function接口 {public stati ...
最新文章
- SCons — 程序构建工具
- WPF中用于嵌入其他进程窗口的自定义控件(AppContainer)
- 职称不用计算机考试吗,中级职称计算机考试需要什么条件可不用考试
- 如果你扯了团队后腿,你应该内疚
- const 一级指针的启示
- ICCV 2019 | 首个镜子分割网络问世,大连理工、鹏城实验室、香港城大出品
- 108扫货节完美收官 在线交易额破千万
- Excel 2010 VBA 入门 126 批量设置控件属性
- steam怎么设置邮箱令牌_【教程】电脑端steam动态令牌设置教程
- 计算机组装流程是什么,组装电脑的步骤
- OpenCV判断图片是否是黑白图片
- Python GUI工具——取色器
- 集成电路模拟版图入门——转行版图基础学习笔记(一)
- 张曦予巴黎时装周儿童单元T台走秀演绎童真风采
- 【大话设计模式-2】UML 类图的绘制(源码案例分析)
- 进阶C语言------>字符函数和字符串函数------strcmp函数
- 「重磅」2020下半年中国移动互联网大报告
- Linux离线安装Kafka(超级精简亲测安装)
- linux代码怎么运行gedit,Linux中gedit命令起什么作用呢?
- java开发框架emp入门教程,实战解析