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.
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.
{ 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.
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:
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!