Does javac ever generate static bridge methods?

时间:2018-01-11 08:41:10

标签: java jvm decompiling

Bridge methods are used in java to handle covariance in derived methods, and to change visibility on derived methods.

However, both of these cases are for instance methods (as you can't derive static methods).

I was looking at how Kotlin generates argument defaults, and I was struck that it uses static bridge methods.

I can't think of a circumstance under which Javac generates static bridge methods - can anyone else? (by this, I mean a method which has the ACC_BRIDGE flag (0x40) set, not just a semantically bridging method)

(fwiw - example code and decompilations (using cfr 0_124 with --hidebridgemethods false))

Variance

public class BridgeTest1Base<T> {
 public T frob() {
    return null;
 }
}

public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
 public Integer frob() {
    return null;
 }
}

decompiles to

public class BridgeTest1Derived extends BridgeTest1Base<Integer> {
 @Override
 public Integer frob() {
    return null;
 }

 @Override
 public /* bridge */ /* synthetic */ Object frob() {
    return this.frob();
 }
}

Visibility

class BridgeTest2Base {
    public void frob() {
    }
}

public class BridgeTest2Derived extends BridgeTest2Base {}

decompiles to

public class BridgeTest2Derived extends BridgeTest2Base {
 @Override
 public /* bridge */ /* synthetic */ void frob() {
    super.frob();
 }
}

Kotlin defaults - 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());
}
}

decompiles (into java) to (note the static bridge)

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

3 个答案:

答案 0 :(得分:4)

根据Java语言规范的桥接方法(应该使用ACC_BRIDGE注释的方法)确保覆盖兼容签名,以便使用方法调用方法即使在字节码级别具有不同的方法签名,原始签名也将以重写方法结束。 Java编程语言中唯一的应用程序是类型擦除和协变返回类型。

由于无法以调用者重定向的方式覆盖static方法,因此不存在static可能出现Java语言规范意义上的桥接方法的情况。方法。因此,javac永远不会生成设置ACC_BRIDGEACC_STATIC的方法。

将方法标记为代表另一种语言语义的桥接方法也是一种非常可疑的行为。正如the JVM specification所说:

  

ACC_BRIDGE标志用于指示编译器为Java编程语言生成的桥接方法。

还有其他合成委托方法可能是static,如嵌套类访问器或方法引用适配器(例如 varargs 或交集类型)。这些不算作桥接方法。

答案 1 :(得分:2)

AFAIK javac目前不会生成 static 的桥接方法,这并不意味着将来不会出现桥接方法是静态的(我不知道)甚至是一个假设的例子。)

在两种情况下使用桥接方法:当您在显示它时处理泛型时,在覆盖时处理协变返回类型。

在第一种情况下,在实现通用接口或扩展泛型类时会创建桥接方法。

对于接口,当覆盖泛型方法(必须是非静态的)时,会创建一个桥接方法,但由于它将委托给非静态,所以本身是非静态的。好吧,java-8允许在接口内部使用静态方法,但是,它们是静态的,它们是不可覆盖的(它们甚至不是继承的,而不是来自类的静态方法)。

对于通用类,同样的故事。只有实例方法是可覆盖的,即使创建了这样的桥接方法,因为它将调用非静态方法,它本身就是这样。值得注意的是,类中的静态方法是继承的,但它们不可覆盖(与接口方法相反),因此没有桥接方法。

最后一个例子是协变返回类型:

static class Parent {

}

static class Child extends Parent {

}

static class First {
    public Parent go() {
        return new Parent();
    }
}

static class SubFirst extends First {
    @Override
    public Child go() {
        return new Child();
    }
}

这里将在SubFirst中创建一个桥接方法,但由于你只能覆盖实例方法(我已经说了多少次?),因此不需要将bridge方法本身设置为静态。

答案 2 :(得分:0)

是的,javac生成静态桥接方法,允许内部类访问私有外部类方法。

以此来源为例:

package bridge;

public class C {
    private void doIt() {
    }

    class D {
        {
            doIt();
        }
    }
}

Javap向您展示了生成的静态access$0方法:

$ javap -p -cp target/classes bridge.C
Compiled from "C.java"
public class bridge.C {
  public bridge.C();
  private void doIt();
  static void access$0(bridge.C);
}

这是内容:

  static void access$0(bridge.C);
    descriptor: (Lbridge/C;)V
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #17                 // Method doIt:()V
         4: return