您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java橋接方法怎么使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java橋接方法怎么使用”吧!
橋接方法是jdk1.5引入泛型后,為使java泛型方法生成的字節碼與jdk1.5版本之前的字節碼兼容由編譯器自動生成的。
可用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();
那么get方法入參只能為String變量,因為編譯器在編譯期間會進行類型校驗,不符合類型將直接報編譯失敗。
{ 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; }
為了能夠正確的編譯,可以看到源碼中父類SuperClass get方法參數類型為T(T t),而在字節碼層面可以看到,經過編譯后,get方法入參和返回值類型都為Object。
可以想象一下,如果沒有編譯器自動生成的橋接方法,那么編譯是不會通過的。父類SubClass get方法經過編譯后入參和返回值類型都為Object,而子類get方法入參和返回值類型為String,子類并沒有重寫父類的get方法(重寫:訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變)。所有編譯器需要生成一個橋接方法,Object get(Object) 就可以編譯通過了。
主要借助Spring的BridgeMethodResolver#findBridgedMethod找到被橋接的方法,原理是首先找到類聲明的所有方法,找到與橋接方法簡單名稱和方法參數數量相同的候選方法,若只要一個則直接返回,若有多個則循環判斷方法參數類型是否相同或者候選方法都有相同的方法簽名則從其中任選一個方法作為被橋接的方法。
@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()); } }
輸出如下:
感謝各位的閱讀,以上就是“Java橋接方法怎么使用”的內容了,經過本文的學習后,相信大家對Java橋接方法怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。