繁体   English   中英

Java泛型 - 桥接方法?

[英]Java Generics - Bridge method?

与 Java 泛型相关的称为“桥接方法”的概念让我停下来思考它。

顺便说一句,我只知道它发生在字节码级别,无法供我们使用。

但我很想知道 Java 编译器使用的“桥接方法”背后的概念。

幕后究竟发生了什么以及为什么使用它?

对示例的任何帮助将不胜感激。

它是一种方法,允许扩展泛型类或实现泛型接口(具有具体类型参数)的类仍用作原始类型。

想象一下:

public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }
}

这不能以其原始形式使用,传递两个Object进行比较,因为这些类型被编译到 compare 方法中(与泛型类型参数 T 会发生的情况相反,其中类型将被擦除)。 因此,在幕后,编译器添加了一个“桥接方法”,它看起来像这样(如果它是 Java 源代码):

public class MyComparator implements Comparator<Integer> {
   public int compare(Integer a, Integer b) {
      //
   }

   //THIS is a "bridge method"
   public int compare(Object a, Object b) {
      return compare((Integer)a, (Integer)b);
   }
}

编译器保护对桥接方法的访问,强制直接对其进行显式调用会导致编译时错误。 现在该类也可以以其原始形式使用:

Object a = 5;
Object b = 6;

Comparator rawComp = new MyComparator();
int comp = rawComp.compare(a, b);

为什么还需要它?

除了添加对显式使用原始类型的支持(主要是为了向后兼容)之外,还需要桥接方法来支持类型擦除。 使用类型擦除,这样的方法:

public <T> T max(List<T> list, Comparator<T> comp) {
   T biggestSoFar = list.get(0);
   for ( T t : list ) {
       if (comp.compare(t, biggestSoFar) > 0) {
          biggestSoFar = t;
       }
   }
   return biggestSoFar;
}

实际上被编译成与此兼容的字节码:

public Object max(List list, Comparator comp) {
   Object biggestSoFar = list.get(0);
   for ( Object  t : list ) {
       if (comp.compare(t, biggestSoFar) > 0) {  //IMPORTANT
          biggestSoFar = t;
       }
   }
   return biggestSoFar;
}

如果桥接方法不存在并且您将List<Integer>MyComparator给此函数,则标记为IMPORTANT的行的调用将失败,因为MyComparator将没有称为compare方法,该方法需要两个Object ......只有一个这需要两个Integer s。

下面的常见问题是一个很好的阅读。

也可以看看:

如果您想了解为什么需要桥接方法,您最好了解没有它会发生什么。 假设没有桥接方法。

class A<T>{
  private T value;
  public void set(T newVal){
    value=newVal
  }
}

class B extends A<String>{
  public void set(String newVal){
    System.out.println(newVal);
    super.set(newVal);
  }
}

请注意,擦除后, A set方法变为public void set(Object newVal)因为类型参数T没有限制。 B没有方法的签名与A set的相同。 所以没有覆盖。 因此,当这样的事情发生时:

A a=new B();
a.set("Hello World!");

多态在这里不起作用。 请记住,您需要在子类中覆盖父类的方法,以便您可以使用父类 var 来触发多态。

桥接方法的作用是使用来自同名但签名不同的方法的所有信息静默覆盖父类中的方法。 在桥接方法的帮助下,多态起作用了。 尽管从表面上看,您使用不同签名的方法覆盖了父类方法。

MyComparator的是,编译器推断MyComparator的方法:

public int compare(Integer a, Integer b) {/* code */}

正在尝试覆盖Comparator<T>

public int compare(T a, T b);

来自声明的类型Comparator<Integer> 否则, MyComparatorcompare将被编译器视为附加(重载)而不是覆盖方法。 因此,不会为其创建桥接方法。

正如本文本文所指出的,Java 桥接方法的关键原因是类型擦除多态性

我们以类ArrayDeque源代码)为例,它包含一个clone()方法如下,因为类ArrayDeque实现了Cloneable接口,所以它必须覆盖Object.clone()方法。

public class ArrayDeque<E> extends AbstractCollection<E>
                        implements Deque<E>, Cloneable, Serializable
{

  public ArrayDeque<E> clone() {
    ....
  }
}

ArrayDeque的UML层次图

但问题是ArrayDeque.clone()的返回类型是ArrayDeque<E> ,它与父Object.clone()定义的方法签名不匹配,而在Object.java 中,返回类型是Object

public class Object {

    protected native Object clone() throws CloneNotSupportedException;
}

返回类型不匹配是Polymorphism 的一个问题。 所以在编译结果文件ArrayDeque.class ,Java 编译器生成了两个clone()方法,一个匹配源代码中的签名,另一个匹配父类Object.clone()的签名。

  1. clone()方法返回ArrayDeque<E> ,它是根据对应的源代码生成的
  2. clone()方法返回Object ,它是基于Object.clone()生成的。 这个方法除了调用另一个clone()方法之外什么都不做。 并且,此方法被标记为ACC_BRIDGE ,这表明此方法是由编译器为 Bridge 目的生成的。

基于ArrayDeque.class生成的UML图

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM