lab27
lab27

Reputation: 513

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

Upvotes: 7

Views: 495

Answers (4)

YujiSoftware
YujiSoftware

Reputation: 1612

See the tutorial on bridge methods… @Holger
comment 83410934_48213426

I simplified this document and tried it.

public class Main {
    public static abstract class Node<T> {
        public abstract void setData(T data);
    }

    public static class MyNode extends Node<Integer> {
        private Integer data;

        @Override
        public void setData(Integer data) {
            this.data = data;
        }
    }
}

Run javac Main.java, and javap -v 'Main$MyNode'.
As a result, found ACC_BRIDGE!

public void setData(java.lang.Object);
  descriptor: (Ljava/lang/Object;)V
  flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
  Code:
    stack=2, locals=2, args_size=2
        0: aload_0
        1: aload_1
        2: checkcast     #13                 // class java/lang/Integer
        5: invokevirtual #15                 // Method setData:(Ljava/lang/Integer;)V
        8: return
    LineNumberTable:
      line 6: 0

public void setData(java.lang.Integer);
  descriptor: (Ljava/lang/Integer;)V
  flags: (0x0001) ACC_PUBLIC
  Code:
    stack=2, locals=2, args_size=2
        0: aload_0
        1: aload_1
        2: putfield      #7                  // Field data:Ljava/lang/Integer;
        5: return
    LineNumberTable:
      line 11: 0
      line 12: 5

Upvotes: 0

Holger
Holger

Reputation: 298459

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.

Upvotes: 4

Eugene
Eugene

Reputation: 120978

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.

Upvotes: 2

Erwin Bolwidt
Erwin Bolwidt

Reputation: 31299

Yes, javac generates static bridge methods to allow inner classes to access private outer class methods.

Take for example this source:

package bridge;

public class C {
    private void doIt() {
    }

    class D {
        {
            doIt();
        }
    }
}

Javap shows you the static access$0 method that was generated:

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

It's content:

  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

Upvotes: 1

Related Questions