Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does javac ever generate static bridge methods?

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());
}
}
like image 715
lab27 Avatar asked Jan 11 '18 08:01

lab27


People also ask

What are bridge methods in Java?

Bridge methods are synthetic methods created by the compiler as part of the erasure process when compiling a class or interface that extends a parameterized class or implements a parameterized interface.

What is the bridge method?

When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, which is called a bridge method, as part of the type erasure process.


2 Answers

Bridge methods according to the Java Language Specification, which are the methods that should be annotated with ACC_BRIDGE, are there to ensure an override compatible signature, so that code invoking the method using the original signature will end up at the overridden method even if it has a different method signature at the bytecode level. The only applications in the Java programming language are type erasure and covariant return types.

Since static methods can not be overridden in that way that a caller could get redirected, there is no scenario in which a bridge method in the sense of the Java Language Specification can occur for a static method. Therefore, javac never produces a method that has ACC_BRIDGE and ACC_STATIC set.

It’s also a very questionable behavior to mark methods as bridge method on behalf of another language’s semantics. As the JVM specification says:

The ACC_BRIDGE flag is used to indicate a bridge method generated by a compiler for the Java programming language.

There are other synthetic delegate methods that might be static, like nested class accessors or adapters for method references (e.g. for varargs or intersection types). These do not count as bridge methods.

like image 147
Holger Avatar answered Sep 29 '22 13:09

Holger


AFAIK javac will not generate bridge methods as static at the moment, this does not mean that in future there will no case for the bridge methods to be static (I am not aware of even a hypothetical example though).

Bridge methods are used in two cases : when you are dealing with generics as you have shown it and covariant return types when overriding.

In the first case, bridge methods are created when you implement a generic interface or extend a generic class.

For interfaces, when you override a generic method (which has to be non-static), there will be a bridge method created, but since it will delegate to the non-static one, it is itself non-static. Well, java-8, allows static method inside interfaces, but, being static, these are non-overridable (they are not even inherited, as opposed to static methods from classes).

For generic classes, same story stands. Only instance methods are overridable, and even if such a bridge method is created, since it will call a non-static method, it will itself be such. The small caveat is that static methods from classes are inherited, but they are not overridable (as opposed to interface ones), thus no bridge methods.

The last example is covariant return types:

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

Here a bridge method will be created in SubFirst, but since you can only override instance methods (how many times have I said this already?), there is no need for the bridge method to be itself static.

like image 40
Eugene Avatar answered Sep 29 '22 12:09

Eugene