java中使用桥方法来处理派生方法中的协方差,并更改派生方法的可见性。
但是,这两种情况都是针对instance方法的(因为不能派生静态方法)。
我正在研究Kotlin如何生成参数默认值,我惊讶地发现它使用了static桥接方法。
- 我想不出Javac生成静态桥方法的情况-其他人可以吗?* (这里,我指的是设置了ACC_BRIDGE标志(0x 40)的方法,而不仅仅是语义桥接方法)
(fwiw- 示例代码和反编译(使用cfr 0_124和--hidebridgemethods false)
差异
public class BridgeTest1Base<T> {
public T frob() {
return null;
}
}
public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
public Integer frob() {
return null;
}
}
反编译为
public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
@Override
public Integer frob() {
return null;
}
@Override
public /* bridge */ /* synthetic */ Object frob() {
return this.frob();
}
}
可见度
class BridgeTest2Base {
public void frob() {
}
}
public class BridgeTest2Derived extends BridgeTest2Base {}
反编译为
public class BridgeTest2Derived extends BridgeTest2Base {
@Override
public /* bridge */ /* synthetic */ void frob() {
super.frob();
}
}
Kotlin默认值- yum!
class frob2() {
fun fred2(x: Int = 300, y: frob2 = mkFrob2(x)) {
println("{this}{x}{y}")
}
fun mkFrob2(x: Int): frob2 {
return this;
}
fun foobar() {
fred2();
fred2(100);
fred2(100, frob2());
}
}
反编译(到java)到(注意静态桥)
public final class frob2 {
public final void fred2(int x, @NotNull frob2 y) {
Intrinsics.checkParameterIsNotNull((Object)y, (String)"y");
String string = "{this}{x}{y}";
System.out.println((Object)string);
}
public static /* bridge */ /* synthetic */ void fred2$default(frob2 frob22, int n, frob2 frob23, int n2, Object object) {
if ((n2 & 1) != 0) {
n = 300;
}
if ((n2 & 2) != 0) {
frob23 = frob22.mkFrob2(n);
}
frob22.fred2(n, frob23);
}
@NotNull
public final frob2 mkFrob2(int x) {
return this;
}
public final void foobar() {
frob2.fred2$default(this, 0, null, 3, null);
frob2.fred2$default(this, 100, null, 2, null);
this.fred2(100, new frob2());
}
}
4条答案
按热度按时间byqmnocz1#
根据Java语言规范,桥接方法应该使用
ACC_BRIDGE
进行注解,以确保 override compatible signature,因此使用原始签名调用方法的代码将在被覆盖的方法处结束,即使它在字节码级别具有不同的方法签名。Java编程语言中唯一的应用是类型擦除和协变返回类型。由于
static
方法不能以调用者可能被重定向的方式被重写,因此在Java语言规范的意义上,static
方法不会出现桥方法。因此,javac
永远不会产生一个包含ACC_BRIDGE
和ACC_STATIC
的方法。代表另一种语言的语义将方法标记为桥方法也是一种非常可疑的行为。正如JVM规范所说:
ACC_BRIDGE
标志用于指示由Java编程语言的编译器生成的桥方法。还有其他合成委托方法可能是
static
,比如嵌套类访问器或方法引用的适配器(例如for varargs or intersection types)。这些方法不算作桥接方法。dpiehjr42#
AFAIK
javac
目前不会将桥方法生成为 static,这并不意味着将来不会有桥方法是静态的(尽管我不知道甚至是假设的例子)。桥接方法用于两种情况:当你处理泛型时,正如你所展示的,当重写时,协变返回类型。
在第一种情况下,桥方法是在实现泛型接口或扩展泛型类时创建的。
对于接口,当你覆盖一个泛型方法(必须是非静态的)时,会创建一个桥方法,但是因为它会委托给 * 非静态 * 方法,所以它本身也是非静态的。Java-8允许在接口中使用静态方法,但是,由于是静态的,这些方法是不可重写的(它们甚至不能被继承,而不是来自类的静态方法)。
对于泛型类,情况也是一样。只有 instance 方法是可重写的,即使创建了这样一个桥方法,由于它将调用一个非静态方法,它本身也是这样。需要注意的是,类的静态方法是继承的,但它们是不可重写的(与接口方法相反),因此没有桥接方法。
最后一个例子是协变返回类型:
这里将在
SubFirst
中创建一个桥接方法,但是由于您只能覆盖示例方法(我已经说过多少次了?),则桥方法本身不需要是静态的。pcrecxhr3#
是的,javac生成静态桥方法来允许内部类访问私有的外部类方法。
以这个来源为例:
Javap向您展示了生成的静态
access$0
方法:它的内容:
sd2nnvve4#
参见the tutorial on bridge methods...@Holger
评论83410934_48213426
我简化了这个文件并尝试了一下。
运行
javac Main.java
和javap -v 'Main$MyNode'
。结果发现
ACC_BRIDGE!