[Translation] Java bridge method in detail

Detailed explanation of Java bridge methods

A bridge method in Java is a synthetic method that is necessary to implement certain Java language features. The most well-known examples are covariant return value types and generic erasure, which cause the parameters of the base class method to be inconsistent with the parameter types of the method actually called.

Take a look at the following examples:

public class SampleOne {
    public static class A<T> {
        public T getT() {
            return null;
        }
    }

    public static class  B extends A<String> {
        public String getT() {
            return null;
        }
    }
}

In fact this is an example of a covariant return type, which after generic erasure will turn into a code snippet similar to the following:

public class SampleOne {
    public static class A {
        public Object getT() {
            return null;
        }
    }

    public static class  B extends A {
        public String getT() {
            return null;
        }
    }
}

After decompiling the compiled bytecode, the class Bwill look like this:

public class SampleOne$B extends SampleOne$A {
public SampleOne$B();
...
public java.lang.String getT();
Code:
0:   aconst_null
1:   areturn
public java.lang.Object getT();
Code:
0:   aload_0
1:   invokevirtual   #2; // 调用 getT:()Ljava/lang/String;
4:   areturn
}

As you can see from the above, there is a new synthetic method java.lang.Object getT(), which is not present in the source code. This method acts as a bridge, all it does is delegate calls to itself to the method jva.lang.String getT(). The compiler has to do this because in JVM methods, the return type is also part of the method signature, and the creation of bridge methods is exactly how covariant return types are implemented.

Now take a look at the following example related to generics:

public class SampleTwo {
    public static class A<T> {
        public T getT(T args) {
            return args;
        }
    }

    public static class B extends A<String> {
        public String getT(String args) {
            return args;
        }
    }
}

The compiled class Bwill look like this:

public class SampleThree$B extends SampleThree$A{
public SampleThree$B();
...
public java.lang.String getT(java.lang.String);
Code:
0:   aload_1
1:   areturn

public java.lang.Object getT(java.lang.Object);
Code:
0:   aload_0
1:   aload_1
2:   checkcast       #2; //class java/lang/String
5:   invokevirtual   #3; //Method getT:(Ljava/lang/String;)Ljava/lang/String;
8:   areturn
}

The bridge method here overrides Athe method, not only delegating calls to itself to the base class Amethod with a string argument, but also performing java.lang.Stringa (#2). This means that if you run code like the following, ignoring the compiler's "unchecked" warning, the result will be an exception thrown from the bridged method ClassCastException.

A a = new B();
a.getT(new Object()));

The above examples are two of the most well-known use cases for bridge methods, but there is at least one use case where bridge methods are used to "change" base class visibility. Consider the following sample code and take a guess if the compiler needs to create a bridge method:

package samplefour;

public class SampleFour {
    static class A {
        public void foo() {
        }
    }
    public static class C extends A {

    }
    public static class D extends A {
        public void foo() {
        }
    }
}

If you decompile Cthe class , you'll see that there are foomethods, which override the base class's method and delegate calls to itself (the base class's method):

public class SampleFour$C extends SampleFour$A{
...
public void foo();
Code:
0:   aload_0
1:   invokespecial   #2; //Method SampleFour$A.foo:()V
4:   return

}

The compiler needs such a method because the Aclass is not public and Ais not visible outside the package where the class is located, but the Cclass is public and all the methods it inherits should be visible outside the package where it is located. It 's important to note that the Dclass won't have a bridge method generated because it overrides the foomethod , so there's no need to "raise" its visibility. This bridging method seems to be introduced due to this bug (fixed in Java 6). This means that such a bridge method will not be generated before Java 6, so it C#foois not possible to use reflection calls outside its package, so that the following code will throw IllegalAccessExceptionan exception when the Java version is less than 1.6.

package samplefive;
...
SampleFour.C.class.getMethod("foo").invoke(new SampleFour.C());
...

Without using the reflection mechanism, normal calls will work.

There may be other cases where the bridging method is used, but there are no relevant sources of information. Also, there is no clear definition of bridging methods, although you can easily guess that, like the above example is fairly obvious, but it would be better if there were some specification that made bridging methods clear. Although methods have been exposed as a reflection API Method#isBridge()since Java 5 , and the bridge flags are part of the bytecode file format , neither the Java Virtual Machine nor the Java Language Specification have any exact documentation on bridge methods, nor do they provide information on compiling Any rules on when/how the bridging method is used. All I can find is a reference to the "discussion board" here .


The Nuggets Translation Project is a community that translates high-quality Internet technical articles. The source of the articles is the English sharing articles on Nuggets. The content covers Android , iOS , front-end , back- end , blockchain , products , design , artificial intelligence and other fields. If you want to see more high-quality translations, please continue to pay attention to the Nuggets translation plan , official Weibo , and Zhihu column .

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326691971&siteId=291194637