当前位置:首页 > IT技术 > 其他 > 正文

泛型之桥接方法
2022-08-29 23:57:52

 什么是桥接方法

  在 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/

开通会员,享受整站包年服务立即开通 >