桥接方法?

8mmmxcuj  于 2021-07-03  发布在  Java
关注(0)|答案(4)|浏览(343)

一些与java泛型相关的叫做“桥方法”的概念让我停下来仔细考虑一下。
顺便说一句,我只知道它发生在字节码级别,不可供我们使用。
但是我很想知道java编译器使用的“桥方法”背后的概念。
幕后到底发生了什么,为什么要用?
如能提供任何帮助,我们将不胜感激。

oyjwcjzk

oyjwcjzk1#

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

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

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

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

多态性在这里不起作用。记住,您需要重写子类中父类的方法,以便可以使用父类var触发多态性。
网桥方法所做的是使用来自同名但签名不同的方法的所有信息,以静默方式重写父类中的方法。在桥接方法的帮助下,多态性起了作用。虽然在表面上,您使用不同签名的方法重写父类方法。

nimxete2

nimxete22#

正如本文和本文所指出的,java桥方法的关键原因是类型擦除和多态性。
让我们以类arraydeque(源代码)为例,它包含 clone() 方法如下,因为类 ArrayDeque 实现 Cloneable 所以它必须重写 Object.clone() 方法。

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

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


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

public class Object {

    protected native Object clone() throws CloneNotSupportedException;
}

返回类型不匹配是多态性的一个问题。所以在编译的结果文件中 ArrayDeque.class ,java编译器生成了两个 clone() 方法,一个匹配源代码中的签名,另一个匹配父类中的签名 Object.clone() .
clone()方法返回 ArrayDeque<E> ,它是根据相应的源代码生成的
clone()方法返回 Object ,它是基于 Object.clone() . 这个方法除了调用另一个以外什么也不做 clone() 方法。并且,这个方法被标记为acc_bridge,这表示这个方法是由编译器为桥接目的生成的。

omjgkv6w

omjgkv6w3#

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

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

这不能用在它的原始形式中,传递2 Object s进行比较,因为类型被编译到compare方法中(与泛型类型参数t相反,在泛型类型参数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 s、 …只有一个需要两个 Integer s。
下面的常见问题是一个很好的阅读。

另请参见:

泛型常见问题-什么是桥接方法?
java桥方法解释(感谢@bozho)

cfh9epnr

cfh9epnr4#

值得注意的是,编译器推断 MyComparator 的方法:

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

正在试图覆盖 Comparator<T>

public int compare(T a, T b);

从声明的类型 Comparator<Integer> . 否则, MyComparatorcompare 将被编译器视为附加(重载)方法,而不是重写方法。因此,没有为它创建桥接方法。

相关问题