Java Generics - Bridge方法?

时间:2011-02-15 17:48:40

标签: java generics

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

  不过,我只知道它发生在   字节码级别,不可用   供我们使用。

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

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

非常感谢任何有关示例的帮助。

4 个答案:

答案 0 :(得分:72)

这是一个允许扩展泛型类或实现泛型接口(具有具体类型参数)的类仍然可以用作原始类型的方法。

想象一下:

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

这不能以原始形式使用,传递两个Object来进行比较,因为类型被编译到比较方法中(与通用类型参数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);
   }
}

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

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 s ...只需要两个Integer s。

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

另见:

答案 1 :(得分:2)

如果你想了解为什么需要桥接方法,你就能更好地理解没有它的情况。假设没有桥接方法。

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);
  }
}

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

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

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

什么桥接方法无效地覆盖父类中的方法,其中包含来自具有相同名称但具有不同签名的方法的所有信息。在桥接方法的帮助下,多态性起作用。虽然从表面上看,您使用不同签名的方法覆盖父类方法。

答案 2 :(得分:0)

有兴趣注意编译器推断那个MyComparator的方法:

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

正试图覆盖Comparator<T>

public int compare(T a, T b);

来自声明的类型Comparator<Integer>。否则,MyComparator的{​​{1}}将被编译器视为附加(重载)而非覆盖的方法。因此,没有为它创建桥接方法。

答案 3 :(得分:0)

this articlethis article所示,Java桥接方法的主要原因是Type ErasurePolymorphism

我们以类ArrayDequesource code)为例,它包含一个clone()方法,因为类ArrayDeque实现了Cloneable接口因此它必须覆盖Object.clone()方法。

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

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

UML Hierarchy Diagram of ArrayDeque

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

public class Object {

    protected native Object clone() throws CloneNotSupportedException;
}

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

  1. clone()方法返回ArrayDeque<E>,它是根据相应的源代码生成的
  2. clone()方法返回基于Object生成的Object.clone()此方法除了调用另一个clone()方法外无所事事。而且,该方法被标记为ACC_BRIDGE,表示此方法是由编译器出于Bridge目的生成的。

UML diagram generated based on ArrayDeque.class