Home  >  Article  >  Java  >  How to use Java bridging method

How to use Java bridging method

王林
王林forward
2023-04-20 13:07:111050browse

1. Introduction to the bridge method

The bridge method is after the introduction of generics in jdk1.5. In order to make the bytecode generated by the java generic method compatible with the bytecode before jdk1.5 version, the compiler Automatically generated.

Availablemethod.isBridge()To determine whether the method is a bridge method, there will be flags marked ACC_BRIDGE, ACC_SYNTHETIC in the generated bytecode, according to a picture from In-depth Understanding of Java Virtual Machine From the access flag diagram, you can see that the ACC_BRIDGE representation method is a bridge method generated by the compiler, and the ACC_SYNTHETIC representation method is automatically generated by the compiler and does not belong to the source code.

How to use Java bridging method

2. When will a bridge method be generated?

When a subclass inherits the parent class (inherited interface) to implement an abstract generic method, the compiler will Automatically generate bridge methods for subclasses

#父类
public abstract class SuperClass<T> {

  public abstract T get(T t) ;
}


#子类
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }
}

Use the javap -v SubClass.class command to view the bytecode of the class SubClass:

Classfile /Users/xudong/project-maven/test/person-study/dubbo-provider/target/classes/com/monian/dubbo/provider/study/generic/SubClass.class
  Last modified 2022年7月25日; size 777 bytes
  MD5 checksum 1328a7043cde4b809a156e7a239335a6
  Compiled from "SubClass.java"
public class com.monian.dubbo.provider.study.generic.SubClass extends com.monian.dubbo.provider.study.generic.SuperClass<java.lang.String>
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #4                          // com/monian/dubbo/provider/study/generic/SubClass
  super_class: #5                         // com/monian/dubbo/provider/study/generic/SuperClass
  interfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:
   #1 = Methodref          #5.#23         // com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
   #2 = Class              #24            // java/lang/String
   #3 = Methodref          #4.#25         // com/monian/dubbo/provider/study/generic/SubClass.get:(Ljava/lang/String;)Ljava/lang/String;
   #4 = Class              #26            // com/monian/dubbo/provider/study/generic/SubClass
   #5 = Class              #27            // com/monian/dubbo/provider/study/generic/SuperClass
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/monian/dubbo/provider/study/generic/SubClass;
  #13 = Utf8               get
  #14 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #15 = Utf8               s
  #16 = Utf8               Ljava/lang/String;
  #17 = Utf8               MethodParameters
  #18 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;
  #19 = Utf8               Signature
  #20 = Utf8               Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
  #21 = Utf8               SourceFile
  #22 = Utf8               SubClass.java
  #23 = NameAndType        #6:#7          // "<init>":()V
  #24 = Utf8               java/lang/String
  #25 = NameAndType        #13:#14        // get:(Ljava/lang/String;)Ljava/lang/String;
  #26 = Utf8               com/monian/dubbo/provider/study/generic/SubClass
  #27 = Utf8               com/monian/dubbo/provider/study/generic/SuperClass
{
  public com.monian.dubbo.provider.study.generic.SubClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method com/monian/dubbo/provider/study/generic/SuperClass."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;

  public java.lang.String get(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1
         1: areturn
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;
            0       2     1     s   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      s

  public java.lang.Object get(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #2                  // class java/lang/String
         5: invokevirtual #3                  // Method get:(Ljava/lang/String;)Ljava/lang/String;
         8: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/monian/dubbo/provider/study/generic/SubClass;
    MethodParameters:
      Name                           Flags
      s                              synthetic
}
Signature: #20                          // Lcom/monian/dubbo/provider/study/generic/SuperClass<Ljava/lang/String;>;
SourceFile: "SubClass.java"

You can see the bytecode There are two get methods. The second method parameter and return value type are both java.lang.Object and you can see that the flags have corresponding flags ACC_BRIDGE and ACC_SYNTHETIC, indicating that this method is a bridge method automatically generated by the compiler. Look at the code attribute again:

aload_0: Load this variable into the operand stack

aload_1: Load the method variable s into the operand stack

checkcast # 2: Verify whether the variable s on the top of the stack is of type java.lang.String

invokevirtual # 3: Call the method public String get(String s)

areturn: Return the result

according to From the above code explanation, we can see that the bridge method generated by the compiler looks like this. The bridge method actually calls the actual generic method

public String get(String s) {
 return s;
}

#桥接方法
public Object get(Object s) {
  return get((String) s);
}

Generics-type erasure

public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) {
    SuperClass subClass = new SubClass();
    Object s = "hello world";
    System.out.println(subClass.get(s));
  }
}

java Generics will be erased and replaced with non-generic upper bounds at runtime, and the Java virtual machine cannot know the exact type. The above code can be compiled and will call the bridge method of the subclass SubClass. The bridge method then calls the actual generic method. If it is defined as SuperClassf7e83be87db5cd2d9a8a0b8117b38cd4 subClass = new SubClass();Then the get method input parameter can only be a String variable, because the compiler will perform type checking during compilation, and non-compliant types will be directly reported to the compiler fail.

3. Why generate generic methods

{
  public com.monian.dubbo.provider.study.generic.SuperClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SuperClass;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/monian/dubbo/provider/study/generic/SuperClass<TT;>;

  public abstract T get(T);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
    MethodParameters:
      Name                           Flags
      t
    Signature: #18                          // (TT;)TT;
}

In order to compile correctly, you can see that the parameter type of the parent class SuperClass get method in the source code is T (T t), while in the bytecode It can be seen at the level that after compilation, the input and return value types of the get method are both Object.

You can imagine that if there is no bridging method automatically generated by the compiler, then the compilation will not pass. After compilation, the parent class SubClass get method input parameters and return value types are Object, while the subclass get method input parameters and return value types are String. The subclass does not override the get method of the parent class (rewrite: access method The implementation process must be rewritten, and the return value and formal parameters cannot be changed). All compilers need to generate a bridge method, and Object get(Object) can be compiled and passed.

4. Get the actual generic method based on the bridge method

Mainly use Spring's BridgeMethodResolver#findBridgedMethod to find the bridged method. The principle is to first find all the methods declared by the class. Finding and bridging methods is simple. Candidate methods with the same name and number of method parameters. If there is only one, it will be returned directly. If there are more than one, it will loop to determine whether the method parameter types are the same or the candidate methods have the same method signature, and then any one of them will be selected as the bridged method. .

@Slf4j
public class SubClass extends SuperClass<String> {

  @Override
  public String get(String s) {
    return s;
  }

  public static void main(String[] args) throws Exception {

    SubClass subClass = new SubClass();
    Method bridgeMethod = subClass.getClass().getDeclaredMethod("get", Object.class);
    log.info("bridgeMethod is bridge:" + bridgeMethod.isBridge());
    log.info("bridgeMethod:" + bridgeMethod.toString());

    // 实际泛型方法
    Method actualMethod = subClass.getClass().getDeclaredMethod("get", String.class);
    log.info("actualMethod:" + actualMethod.toString());
    // 通过spring #BridgeMethodResolver由桥接方法获取到实际泛型方法
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(bridgeMethod);
    log.info("bridgedMethod:" + bridgedMethod.toString());
  }
}

The output is as follows:

How to use Java bridging method

The above is the detailed content of How to use Java bridging method. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete