브리징 메소드는 jdk1.5 이전의 바이트코드와 자바 제네릭 메소드로 생성된 바이트코드를 호환시키기 위해 jdk1.5에 제네릭이 도입된 후 컴파일러에 의해 자동으로 생성됩니다.
method.isBridge()
를 사용하여 해당 메소드가 브리지 메소드인지 여부를 확인할 수 있습니다. 생성된 바이트코드에는 In-Deep의 액세스 플래그 맵을 기반으로 ACC_BRIDGE, ACC_SYNTHETIC로 표시된 플래그가 있습니다. Java Virtual Machine의 이해 ACC_BRIDGE 표현 방식은 컴파일러에 의해 생성된 브릿지 방식이고, ACC_SYNTHETIC 표현 방식은 컴파일러에 의해 자동 생성된 것으로 소스코드에 속하지 않는다는 것을 알 수 있다. method.isBridge()
判断method是否是桥接方法,在生成的字节码中会有flags标记 ACC_BRIDGE, ACC_SYNTHETIC ,根据来自深入理解java虚拟机的一张访问标志图可以看到 ACC_BRIDGE表示方法是由编译器产生的桥接方法,ACC_SYNTHETIC表示方法由编译器自动产生不属于源码。
当子类继承父类(继承接口)实现抽象泛型方法的时候,编译器会为子类自动生成桥接方法
#父类 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; } }
使用javap -v SubClass.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"
可以看到字节码中有两个get方法,第二个方法参数和返回值类型都是java.lang.Object 并且可以看到flags有相应标志ACC_BRIDGE, ACC_SYNTHETIC说明此方法就是有编译器自动生成的桥接方法。再看code属性:
aload_0:把this变量装载到操作数栈中
aload_1:把方法变量s装载到操作数栈中
checkcast # 2:校验栈顶变量s是否为java.lang.String类型
invokevirtual # 3: 调用方法 public String get(String s)
areturn: 返回结果
根据上述code解释可以看出编译器生成的桥接方法为这个样子的,桥接方法实际上调用了实际的泛型方法
public String get(String s) { return s; } #桥接方法 public Object get(Object s) { return get((String) s); }
泛型-类型擦除
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的泛型在运行时会进行泛型擦除替换成非泛型上边界,java虚拟机无法知道准确的类型。 上述代码能编译通过并且会调用子类SubClass的桥接方法由桥接方法再去调用实际泛型方法。如果定义为SuperClass<String> subClass = new SubClass();
{ 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; }
javap -v SubClass.class
명령을 사용하여 바이트코드를 확인하세요. SubClass 클래스:@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()); } }
checkcast # 2: 스택 맨 위에 있는 변수 s가 java인지 확인합니다. .lang.String type
🎜invokevirtual # 3: public String get(String s)🎜🎜areturn 메소드 호출: 결과 반환 🎜🎜위 코드 설명에 따르면 컴파일러가 생성한 브리지 메소드는 다음과 같습니다. 브리지 메소드는 실제로 실제를 호출합니다. 일반 메소드 🎜rrreee🎜Generics-type erasure🎜rrreee🎜Java의 제네릭은 런타임 시 일반적으로 삭제되고 제네릭이 아닌 상한으로 대체되며 Java 가상 머신은 정확한 유형을 알 수 없습니다. 위의 코드는 컴파일될 수 있으며 서브클래스 SubClass의 브리지 메서드를 호출합니다. 그런 다음 브리지 메서드는 실제 일반 메서드를 호출합니다.SuperClass<string> subClass = new SubClass();</string>
로 정의된 경우 컴파일러는 컴파일 중에 유형 검사를 수행하므로 get 메소드의 입력 매개변수는 String 변수만 될 수 있습니다. 비준수 유형은 컴파일 실패로 직접 보고됩니다. 🎜🎜3. 일반 메소드를 생성하는 이유🎜rrreee🎜올바로 컴파일하려면 소스 코드에서 상위 클래스 SuperClass get 메소드 매개변수 유형이 T(T t)인 것을 볼 수 있으며, 그 이후는 바이트코드 레벨에서 확인할 수 있습니다. 컴파일의 경우 get 메소드의 입력 매개변수와 반환 값 유형은 모두 Object입니다. 🎜🎜컴파일러에서 자동으로 생성한 브리징 메서드가 없으면 컴파일이 통과되지 않을 것이라고 상상할 수 있습니다. 컴파일 후 상위 클래스 SubClass get 메소드 입력 매개변수 및 반환 값 유형은 Object이고 하위 클래스 get 메소드 입력 매개변수 및 반환 값 유형은 String입니다. 하위 클래스는 상위 클래스의 get 메소드를 재정의하지 않습니다(다시 작성: 액세스 메소드는 구현 프로세스를 다시 작성해야 하며 반환 값과 형식 매개변수는 변경할 수 없습니다. 모든 컴파일러는 브리지 메소드를 생성해야 하며 Object get(Object)을 컴파일하고 전달할 수 있습니다. 🎜🎜4. 브리지 메서드를 기반으로 실제 일반 메서드 가져오기 🎜🎜 브리지 메서드를 찾으려면 주로 Spring의 BridgeMethodResolver#findBridgedMethod를 사용합니다. 먼저 클래스에서 선언한 모든 메서드를 찾고 동일한 단순 이름을 가진 후보를 찾는 것입니다. 메서드 매개변수의 수를 브리지 메서드로 지정합니다. 메서드가 하나만 있는 경우 직접 반환됩니다. 메서드가 여러 개 있는 경우 메서드 매개변수 유형이 동일한지 또는 후보 메서드의 메서드 시그니처가 동일한지 확인하기 위해 반복됩니다. , 그 중 하나가 브리지 방법으로 선택됩니다. 🎜rrreee🎜출력은 다음과 같습니다.🎜🎜🎜🎜위 내용은 Java 브리징 방법을 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!