与Java Generics相关的称为“桥接方法”概念的东西让我停下来思考它。
不过,我只知道它发生在 字节码级别,不可用 供我们使用。
但我很想知道Java编译器使用的“桥接方法”背后的概念。
幕后究竟发生了什么以及为何使用它?
非常感谢任何有关示例的帮助。
答案 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 article和this article所示,Java桥接方法的主要原因是Type Erasure和Polymorphism。
我们以类ArrayDeque(source code)为例,它包含一个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()
中的签名匹配。
ArrayDeque<E>
,它是根据相应的源代码生成的Object
生成的Object.clone()
。 此方法除了调用另一个clone()
方法外无所事事。而且,该方法被标记为ACC_BRIDGE,表示此方法是由编译器出于Bridge目的生成的。