Java编程:浅析泛型类型中的桥接方法
Posted 志波同学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>
@Override
public 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
@Override
public 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的桥接方法和BridgeMethodResolver使用