在导航java.lang.reflect.Method
课程期间,我遇到了方法isBridge
。它的Javadoc说它只有在Java规范声明方法为真时才返回true。
请帮我理解这是用来做什么的!如果需要,自定义类可以将其方法声明为桥接吗?
答案 0 :(得分:27)
当扩展其方法具有参数化参数的参数化类型时,编译器可以创建桥接方法。
您可以在此课程BridgeMethodResolver中找到一种方法来获取由“桥接方法”引用的实际方法。
请参阅Create Frame, Synchronize, Transfer Control:
作为这种情况的一个例子,请考虑声明:
class C<T> { abstract T id(T x); }
class D extends C<String> { String id(String x) { return x; } }
现在,给定一个调用
C c = new D();
c.id(new Object()); // fails with a ClassCastException
正在调用的实际方法的擦除,D.id(String)
的签名与编译时方法声明C.id(Object)
的签名不同。前者采用String类型的参数,而后者采用Object类型的参数。在执行方法体之前,调用失败并带有ClassCastException。
只有在程序产生未经检查的警告(§5.1.9)时才会出现这种情况。
实现可以通过创建桥接方法来强制实施这些语义。在上面的示例中,将在D类中创建以下桥接方法:
Object id(Object x) { return id((String) x); }
这是Java虚拟机响应上面显示的调用c.id(new Object())
实际调用的方法,它将根据需要执行强制转换和失败。
另请参阅 Bridge :
如评论中所述,协变覆盖还需要桥接方法:
通常,方法Object clone()
可以被MyObject clone()
覆盖,但编译器将生成桥接方法:
public bridge Object MyObject.clone();
答案 1 :(得分:3)
那里显示的示例(引自JLS)使得听起来像桥接方法仅用于使用原始类型的情况。由于情况并非如此,我认为我将使用桥接方法用于完全类型正确的通用代码的示例。
考虑以下界面和功能:
public static interface Function<A,R> {
public R apply (A arg);
}
public static <A, R> R applyFunc (Function<A,R> func, A arg) {
return func.apply(arg);
}
如果以下列方式使用此代码,则使用桥接方法:
Function<String, String> lower = new Function<String, String>() {
public String apply (String arg) {
return arg.toLowerCase();
}
};
applyFunc(lower, "Hello");
擦除后,Function
接口包含方法apply(Object)Object
(您可以通过反编译字节码来确认)。当然,如果您查看applyFunc
的反编译代码,您会看到它包含对apply(Object)Object
的调用。 Object
是其类型变量的上限,因此没有其他签名有意义。
因此,当使用方法apply(String)String
创建匿名类时,除非创建了桥接方法,否则它实际上不会实现Function
接口。 bridge方法允许所有通用类型的代码使用Function
实现。
有趣的是,只有当类使用签名apply(String)String
实现了一些其他接口时,并且只有当通过该接口类型的引用调用该方法时,编译器才会发出调用签名。
即使我有以下代码:
Function<String, String> lower = ...;
lower.apply("Hello");
编译器仍然会调用apply(Object)Object
。
实际上还有另外一种方法可以让编译器调用apply(String)String
,但它利用了分配给匿名类创建表达式的魔法类型,否则无法写下来:
new Function<String, String>() {
public String apply (String arg) {
return arg.toLowerCase();
}
}.apply("Hello");
答案 2 :(得分:0)
我偶然发现的另一个案例与泛型无关:
protected abstract class Super {
public void m() {}
}
public class Sub extends Super {}
assert Sub.class.getMethod("m").isBridge();