BridgeMethod

The bridging method is a method automatically generated by the compiler in order to make the bytecode generated by the generic method of Java compatible with the bytecode before version 1.5 after the introduction of generics in JDK 1.5. You can judge whether a method is a bridging method through the Method.isBridge() method. The bridging method will be marked as ACC in bytecode_ Bridge and ACC_SYNTHETIC

From the two records intercepted from the jvm specification, you can see that ACC_BRIDGE representation method is a bridge method, while ACC_SYNTHETIC means that the method is not displayed in the source code.

Flag NameValueInterpretation
ACC_BRIDGE0x0040A bridge method, generated by the compiler.
ACC_SYNTHETIC0x1000Declared synthetic; not present in the source code.

What is a bridging method

reference The Java™ Tutorials

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. You normally don't need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.

When compiling a class (or interface) that inherits a generic class (or implements a generic interface), the compiler may need to create a synthesis method (actually a method for type conversion) for type safety processing of generic types. Users don't need to care about this method, but it may be confusing to see this method from the stack.

Through the example on the official website, let's see what the bridging method is

/**
 * Parent class with generic parameters
 */
public class Node<T> {
    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

/**
 * Subclasses using generics
 */
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

Because Java generics will be type erased, this will happen after compilation

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

You can see that Node.setData(T) becomes Node.setData(Object), and the override of the parent method is missing, so it becomes the following

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }
    
    // Bridge method generated by the compiler
    //
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");

        super.setData(data);
    }
}

Through javap, you can see that there is a public void setData(java.lang.Object) method in MyNode class.

$ javap MyNode.class 
Compiled from "MyNode.java"
public class com.st.bytecode.MyNode extends com.st.bytecode.Node<java.lang.Integer> {
  public com.st.bytecode.MyNode(java.lang.Integer);
  public void setData(java.lang.Integer);
  public void setData(java.lang.Object);
}

$ javap -c MyNode.class 
Compiled from "MyNode.java"
public class com.st.bytecode.MyNode extends com.st.bytecode.Node<java.lang.Integer> {
  public com.st.bytecode.MyNode(java.lang.Integer);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #1                  // Method com/st/bytecode/Node."<init>":(Ljava/lang/Object;)V
       5: return

  public void setData(java.lang.Integer);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String MyNode.setData
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_0
       9: aload_1
      10: invokespecial #5                  // Method com/st/bytecode/Node.setData:(Ljava/lang/Object;)V
      13: return

  public void setData(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #6                  // class java/lang/Integer
       5: invokevirtual #7                  // Method setData:(Ljava/lang/Integer;)V
       8: return
}

Why generate bridge methods

In short, the compiler generates the bridge method for compatibility with bytecode before jdk1.5. Because the paradigm was introduced after jdk1.5. Before jdk1.5, for example, set operations are not supported by templates, so the parameters in the generated bytecode are received by Object, so you can also put any type of Object into the set, and the verification of the set type is also dragged to the run time.

However, the paradigm was introduced after jdk1.5, so the content verification of the collection was advanced to the compilation time. However, in order to be compatible with the version before jdk1.5, java uses paradigm erasure, so if the bridging method is not generated, it is incompatible with the bytecode before jdk1.5.

Keywords: Java

Added by DanielStead on Sat, 04 Sep 2021 23:01:09 +0300