简体   繁体   English

Java泛型 - 桥接方法?

[英]Java Generics - Bridge method?

Something called the "bridge method" concept related to Java Generics made me stop at a point and think over it.与 Java 泛型相关的称为“桥接方法”的概念让我停下来思考它。

Btw, I only know that it occurs at the bytecode level and is not available for us to use.顺便说一句,我只知道它发生在字节码级别,无法供我们使用。

But I am eager to know the concept behind the "bridge method" used by the Java compiler.但我很想知道 Java 编译器使用的“桥接方法”背后的概念。

What exactly happens behind the scenes and why it is used?幕后究竟发生了什么以及为什么使用它?

Any help with an example would be greatly appreciated.对示例的任何帮助将不胜感激。

It's a method that allows a class extending a generic class or implementing a generic interface (with a concrete type parameter) to still be used as a raw type.它是一种方法,允许扩展泛型类或实现泛型接口(具有具体类型参数)的类仍用作原始类型。

Imagine this:想象一下:

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

This can't be used in its raw form, passing two Object s to compare, because the types are compiled in to the compare method (contrary to what would happen were it a generic type parameter T, where the type would be erased).这不能以其原始形式使用,传递两个Object进行比较,因为这些类型被编译到 compare 方法中(与泛型类型参数 T 会发生的情况相反,其中类型将被擦除)。 So instead, behind the scenes, the compiler adds a "bridge method", which looks something like this (were it Java source):因此,在幕后,编译器添加了一个“桥接方法”,它看起来像这样(如果它是 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);
   }
}

The compiler protects access to the bridge method, enforcing that explicit calls directly to it result in a compile time error.编译器保护对桥接方法的访问,强制直接对其进行显式调用会导致编译时错误。 Now the class can be used in its raw form as well:现在该类也可以以其原始形式使用:

Object a = 5;
Object b = 6;

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

Why else is it needed?为什么还需要它?

In addition to adding support for explicit use of raw types (which is mainly for backwards compatability) bridge methods are also required to support type erasure.除了添加对显式使用原始类型的支持(主要是为了向后兼容)之外,还需要桥接方法来支持类型擦除。 With type erasure, a method like this:使用类型擦除,这样的方法:

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

is actually compiled into bytecode compatible with this:实际上被编译成与此兼容的字节码:

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

If the bridge method didn't exist and you passed a List<Integer> and a MyComparator to this function, the call at the line tagged IMPORTANT would fail since MyComparator would have no method called compare that takes two Object s...only one that takes two Integer s.如果桥接方法不存在并且您将List<Integer>MyComparator给此函数,则标记为IMPORTANT的行的调用将失败,因为MyComparator将没有称为compare方法,该方法需要两个Object ......只有一个这需要两个Integer s。

The FAQ below is a good read.下面的常见问题是一个很好的阅读。

See Also:也可以看看:

If you want to understand why you need bridge method, you better understand what happens without it.如果您想了解为什么需要桥接方法,您最好了解没有它会发生什么。 Suppose there is no bridge method.假设没有桥接方法。

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

Notice that after erasure, method set in A became public void set(Object newVal) since there is no bound on Type parameter T .请注意,擦除后, A set方法变为public void set(Object newVal)因为类型参数T没有限制。 There is no method in class B the signature of which is the same as set in A .B没有方法的签名与A set的相同。 So there is no override.所以没有覆盖。 Hence, when something like this happened:因此,当这样的事情发生时:

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

Polymorphism won't work here.多态在这里不起作用。 Remember you need to override the method of parent class in child class so that you can use parent class var to trigger polymorphism.请记住,您需要在子类中覆盖父类的方法,以便您可以使用父类 var 来触发多态。

What bridge method does is silently override the method in parent class with all the information from a method with the same name but a different signature.桥接方法的作用是使用来自同名但签名不同的方法的所有信息静默覆盖父类中的方法。 With the help of the bridge method, polymorphism worked.在桥接方法的帮助下,多态起作用了。 Though on the surface, you override the parent class method with a method of different signature.尽管从表面上看,您使用不同签名的方法覆盖了父类方法。

It's insteresting to note that the compiler infers that MyComparator 's method: MyComparator的是,编译器推断MyComparator的方法:

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

is trying to override Comparator<T> 's正在尝试覆盖Comparator<T>

public int compare(T a, T b);

from the declared type Comparator<Integer> .来自声明的类型Comparator<Integer> Otherwise, MyComparator 's compare would be treated by the compiler as an additional (overloading), and not overridding, method.否则, MyComparatorcompare将被编译器视为附加(重载)而不是覆盖方法。 And as such, would have no bridge method created for it.因此,不会为其创建桥接方法。

As indicated by this article and this article , the key reason of the Java bridge method is Type Erasure and Polymorphism .正如本文本文所指出的,Java 桥接方法的关键原因是类型擦除多态性

Let's take the class ArrayDeque ( source code ) as example, it contains a clone() method as bellow, because the class ArrayDeque implements the Cloneable interface so it must override the Object.clone() method.我们以类ArrayDeque源代码)为例,它包含一个clone()方法如下,因为类ArrayDeque实现了Cloneable接口,所以它必须覆盖Object.clone()方法。

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

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

ArrayDeque的UML层次图

But the problem is the return type of ArrayDeque.clone() is ArrayDeque<E> , and it did not match to the method signature defined in the parent Object.clone() , and in Object.java the return type is Object instead.但问题是ArrayDeque.clone()的返回类型是ArrayDeque<E> ,它与父Object.clone()定义的方法签名不匹配,而在Object.java 中,返回类型是Object

public class Object {

    protected native Object clone() throws CloneNotSupportedException;
}

The return type mismatch is a problem for Polymorphism .返回类型不匹配是Polymorphism 的一个问题。 So in the compiled result file ArrayDeque.class , the Java compiler generated two clone() methods, one match the signature in the source code, the other one match to the signature in the parent class Object.clone() .所以在编译结果文件ArrayDeque.class ,Java 编译器生成了两个clone()方法,一个匹配源代码中的签名,另一个匹配父类Object.clone()的签名。

  1. clone() method returns ArrayDeque<E> , which is generated based on the corresponding source code clone()方法返回ArrayDeque<E> ,它是根据对应的源代码生成的
  2. clone() method returns Object , which is generated based on Object.clone() . clone()方法返回Object ,它是基于Object.clone()生成的。 This method is doing nothing but calling the other clone() method.这个方法除了调用另一个clone()方法之外什么都不做。 And, this method is tagged asACC_BRIDGE , which indicates this method is generated by the compiler for the Bridge purpose.并且,此方法被标记为ACC_BRIDGE ,这表明此方法是由编译器为 Bridge 目的生成的。

基于ArrayDeque.class生成的UML图

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

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