<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
橋接方法是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橋接方法的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45