一文搞懂Java橋接方法
1.橋接方法簡(jiǎn)介
橋接方法是jdk1.5引入泛型后,為使java泛型方法生成的字節(jié)碼與jdk1.5版本之前的字節(jié)碼兼容由編譯器自動(dòng)生成的。
可用method.isBridge()判斷method是否是橋接方法,在生成的字節(jié)碼中會(huì)有flags標(biāo)記 ACC_BRIDGE, ACC_SYNTHETIC ,根據(jù)來(lái)自深入理解java虛擬機(jī)的一張?jiān)L問(wèn)標(biāo)志圖可以看到 ACC_BRIDGE表示方法是由編譯器產(chǎn)生的橋接方法,ACC_SYNTHETIC表示方法由編譯器自動(dòng)產(chǎn)生不屬于源碼。

2. 什么時(shí)候會(huì)生成橋接方法
當(dāng)子類(lèi)繼承父類(lèi)(繼承接口)實(shí)現(xiàn)抽象泛型方法的時(shí)候,編譯器會(huì)為子類(lèi)自動(dòng)生成橋接方法
#父類(lèi)
public abstract class SuperClass<T> {
public abstract T get(T t) ;
}
#子類(lèi)
public class SubClass extends SuperClass<String> {
@Override
public String get(String s) {
return s;
}
}使用javap -v SubClass.class命令查看類(lèi)SubClass的字節(jié)碼:
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"可以看到字節(jié)碼中有兩個(gè)get方法,第二個(gè)方法參數(shù)和返回值類(lèi)型都是java.lang.Object 并且可以看到flags有相應(yīng)標(biāo)志ACC_BRIDGE, ACC_SYNTHETIC說(shuō)明此方法就是有編譯器自動(dòng)生成的橋接方法。再看code屬性:
aload_0:把this變量裝載到操作數(shù)棧中
aload_1:把方法變量s裝載到操作數(shù)棧中
checkcast # 2:校驗(yàn)棧頂變量s是否為java.lang.String類(lèi)型
invokevirtual # 3: 調(diào)用方法 public String get(String s)
areturn: 返回結(jié)果
根據(jù)上述code解釋可以看出編譯器生成的橋接方法為這個(gè)樣子的,橋接方法實(shí)際上調(diào)用了實(shí)際的泛型方法
public String get(String s) {
return s;
}
#橋接方法
public Object get(Object s) {
return get((String) s);
}泛型-類(lèi)型擦除
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的泛型在運(yùn)行時(shí)會(huì)進(jìn)行泛型擦除替換成非泛型上邊界,java虛擬機(jī)無(wú)法知道準(zhǔn)確的類(lèi)型。 上述代碼能編譯通過(guò)并且會(huì)調(diào)用子類(lèi)SubClass的橋接方法由橋接方法再去調(diào)用實(shí)際泛型方法。如果定義為SuperClass<String> subClass = new SubClass();那么get方法入?yún)⒅荒転镾tring變量,因?yàn)榫幾g器在編譯期間會(huì)進(jìn)行類(lèi)型校驗(yàn),不符合類(lèi)型將直接報(bào)編譯失敗。
3. 為什么生成泛型方法
{
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;
}為了能夠正確的編譯,可以看到源碼中父類(lèi)SuperClass get方法參數(shù)類(lèi)型為T(mén)(T t),而在字節(jié)碼層面可以看到,經(jīng)過(guò)編譯后,get方法入?yún)⒑头祷刂殿?lèi)型都為Object。
可以想象一下,如果沒(méi)有編譯器自動(dòng)生成的橋接方法,那么編譯是不會(huì)通過(guò)的。父類(lèi)SubClass get方法經(jīng)過(guò)編譯后入?yún)⒑头祷刂殿?lèi)型都為Object,而子類(lèi)get方法入?yún)⒑头祷刂殿?lèi)型為String,子類(lèi)并沒(méi)有重寫(xiě)父類(lèi)的get方法(重寫(xiě):訪(fǎng)問(wèn)的方法的實(shí)現(xiàn)過(guò)程進(jìn)行重新編寫(xiě), 返回值和形參都不能改變)。所有編譯器需要生成一個(gè)橋接方法,Object get(Object) 就可以編譯通過(guò)了。
4. 根據(jù)橋接方法獲取實(shí)際泛型方法
主要借助Spring的BridgeMethodResolver#findBridgedMethod找到被橋接的方法,原理是首先找到類(lèi)聲明的所有方法,找到與橋接方法簡(jiǎn)單名稱(chēng)和方法參數(shù)數(shù)量相同的候選方法,若只要一個(gè)則直接返回,若有多個(gè)則循環(huán)判斷方法參數(shù)類(lèi)型是否相同或者候選方法都有相同的方法簽名則從其中任選一個(gè)方法作為被橋接的方法。
@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());
// 實(shí)際泛型方法
Method actualMethod = subClass.getClass().getDeclaredMethod("get", String.class);
log.info("actualMethod:" + actualMethod.toString());
// 通過(guò)spring #BridgeMethodResolver由橋接方法獲取到實(shí)際泛型方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(bridgeMethod);
log.info("bridgedMethod:" + bridgedMethod.toString());
}
}輸出如下:

以上就是一文搞懂Java橋接方法的詳細(xì)內(nèi)容,更多關(guān)于Java橋接方法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談Java之Map 按值排序 (Map sort by value)
下面小編就為大家?guī)?lái)一篇淺談Java之Map 按值排序 (Map sort by value)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08
SpringBoot注冊(cè)web組件的實(shí)現(xiàn)方式
Servlet是Java Web應(yīng)用程序的基礎(chǔ),它提供了處理客戶(hù)端請(qǐng)求的機(jī)制,Servlet三大組件是指Servlet、Filter和Listener,它們是Java Web應(yīng)用程序的核心組件,本文將給大家介紹一下SpringBoot注冊(cè)web組件的實(shí)現(xiàn)方式,需要的朋友可以參考下2023-10-10
Spring中基于Java的配置@Configuration和@Bean用法詳解
這篇文章主要介紹了Spring中基于Java的配置@Configuration和@Bean用法詳解,Spring中為了減少xml中配置,可以聲明一個(gè)配置類(lèi)(例如SpringConfig)來(lái)對(duì)bean進(jìn)行配置。,需要的朋友可以參考下2019-06-06
PowerJob的AliOssService工作流程源碼解讀
這篇文章主要介紹了PowerJob的AliOssServiceg工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Spring?myBatis數(shù)據(jù)庫(kù)連接異常問(wèn)題及解決

