泛型之桥接方法

 什么是桥接方法

  在 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 方进行了桥接方法的判定。

  我们使用反射调用方法时,也需要考虑桥接方法是否处理。

 

 

 

 

 

  

 

 

posted @   -Lucas  阅读(333)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示