什么是桥接方法
在 JDK1.5 中引入了泛型,泛型类型是基于原始类型、类型擦除原理进行实现的。那么JDK1.5有了泛型怎么兼容JDK1.5之前的类?
原始类型:Java总是会自动的为泛型类型提供一个相应的原始类型。所谓原始类型就是是指泛型的第一个限定类型(从左向右),无限定类型泛型的原始类型默认为Object。
类型擦除:Java中泛型的实现原理是类型擦除(type erasure)。类型擦除是在编译器进行代码编译这个阶段进行的,在编译的时候泛型的类型参数会被原始类型(raw type)所替代。
桥接方法是在父类、子类的继承场景中出现的。父类是泛型类,且在该类中存在泛型方法。子类继承父类,并实现泛型方法。如果在子类实现中不包含父类经过类型擦除后生成的原始类型方法,则编译器会自动将该原始类型方法添加到子类中。这个被添加的原始类型方法我们称之为桥接方法。
看个例子
(1)首先定义一个父类接口SuperClass
public interface SuperClass<T>{ void test(T t); }
我们来看下字节码文件 javap -verbose SuperClass.class,只有一个test方法。
(2)定义一个子类实现类BaseClass
public class BaseClass implements SuperClass<String>{ @Override public void test(String s) { } }
看下BaseClass的字节码文件,发现类中多了一个方法 public void test(java.lang.Object);这个就是本文要讨论的桥接方法。
PS:ACC_BRIDGE 修饰符表示这是一个桥接方法。ACC_SYNTHETIC修饰符表示这个方法是由编译器自动生成的。
(3)定义一个BaseClass的扩展类SubClass(子类),扩展其他方法
public class SubClass extends BaseClass{ public void run() { System.out.println("run---"); } }
查看字节码文件,只有一个run方法。
SubClass重写父类test方法
public class SubClass extends BaseClass{ public void run() { System.out.println("run---"); } @Override public void test(String s) { System.out.println("SubClass.test(s)"); } }
再次查看字节码文件,生成了桥接方法public void test(java.lang.Object);
那么我们是否可以推测出:桥接方法是伴随泛型方法而生的,在继承关系中,如果某个子类覆盖了泛型方法,则编译器会在该子类中自动生成桥接方法。
那么为什么要生成桥接方法?
父接口SuperClass定义了test方法,SuperClass经过类型擦除转换为原始类型后,会生成 public void test(java.lang.Object); 方法,我们在SuperClass的子类定义了泛型的具体类型,导致子类中的 test方法变化为 public void test(String s);,
那么此时父类的 public void test(java.lang.Object); 是没有被实现的,为了解决这个问题,Java 编译器通过桥接的方式实现了 public void test(java.lang.Object); 方法。
这样就保证了SuperClass与子类具有相同的一致的方法 public void test(java.lang.Object); 。在访问泛型对象时,通过父类方法 test进行统一调用,而不需要关注子类的具体实现。
桥接方法的执行过程
找个有桥接方法的类,我们看下字节码文件, 比如BaseClass
public void test(java.lang.Object); 这个方法真正执行的是public void test(String s);
怎么判断一个方法是否是桥接方法?
通过 method.isBridge() 可以判定是否为桥接方法。
public static void main(String[] args) throws Exception{ BaseClass baseClass = new BaseClass(); Method method1 = BaseClass.class.getMethod("test", String.class); method1.invoke(baseClass,"ABC"); System.out.println(method1.isBridge()); Method method2 = BaseClass.class.getMethod("test", Object.class); method2.invoke(baseClass,"ABC"); System.out.println(method2.isBridge()); }
使用场景
在 Mybatis中, MapperAnnotationBuilder 类的 parse 方进行了桥接方法的判定。
我们使用反射调用方法时,也需要考虑桥接方法是否处理。
本文摘自 :https://www.cnblogs.com/