Java中什么是bridge method(橋接方法)

該文完全引自java中什么是bridge method

1秫逝、什么時候會生成橋接方法

就是說一個子類在繼承(或?qū)崿F(xiàn))一個父類(或接口)的泛型方法時渗勘,在子類中明確指定了泛型類型,那么在編譯時編譯器會自動生成橋接方法(當然還有其他情況會生成橋接方法游桩,這里只是列舉了其中一種情況)庄萎。如下所示:

package com.mikan;
 
/**
 * @author Mikan
 * @date 2015-08-05 16:22
 */
public interface SuperClass<T> {
 
    T method(T param);
 
}
 
package com.mikan;
 
/**
 * @author Mikan
 * @date 2015-08-05 17:05
 */
public class SubClass implements SuperClass<String> {
    public String method(String param) {
        return param;
    }
}

來看一下SubClass的字節(jié)碼:

localhost:mikan mikan$ javap -c SubClass.class
Compiled from "SubClass.java"
public class com.mikan.SubClass implements com.mikan.SuperClass<java.lang.String> {
  public com.mikan.SubClass();
    flags: 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/mikan/SubClass;
 
  public java.lang.String method(java.lang.String);
    flags: 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/mikan/SubClass;
               0       2     1 param   Ljava/lang/String;
 
  public java.lang.Object method(java.lang.Object);
    flags: 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 method:(Ljava/lang/String;)Ljava/lang/String;
         8: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lcom/mikan/SubClass;
               0       9     1    x0   Ljava/lang/Object;
}
localhost:mikan mikan$

SubClass只聲明了一個方法种蝶,而從字節(jié)碼可以看到有三個方法扩借,第一個是無參的構(gòu)造方法(代碼中雖然沒有明確聲明襟铭,但是編譯器會自動生成),第二個是我們實現(xiàn)的接口中的方法翁都,第三個就是編譯器自動生成的橋接方法碍论。可以看到flags包括了ACC_BRIDGE和ACC_SYNTHETIC柄慰,表示是編譯器自動生成的方法鳍悠,參數(shù)類型和返回值類型都是Object。再看這個方法的字節(jié)碼坐搔,它把Object類型的參數(shù)強制轉(zhuǎn)換成了String類型藏研,再調(diào)用在SubClass類中聲明的方法,轉(zhuǎn)換過來其實就是:

 public Object method(Object param) {
        return this.method(((String) param));
    }

也就是說概行,橋接方法實際是是調(diào)用了實際的泛型方法蠢挡,來看看下面的測試代碼:

package com.mikan;
 
/**
 * @author Mikan
 * @date 2015-08-07 16:33
 */
public class BridgeMethodTest {
 
    public static void main(String[] args) throws Exception {
        SuperClass superClass = new SubClass();
        System.out.println(superClass.method("abc123"));// 調(diào)用的是實際的方法
        System.out.println(superClass.method(new Object()));// 調(diào)用的是橋接方法
    }
 
}

這里聲明了SuperClass類型的變量指向SubClass類型的實例,典型的多態(tài)。在聲明SuperClass類型的變量時袒哥,不指定泛型類型,那么在方法調(diào)用時就可以傳任何類型的參數(shù)消略,因為SuperClass中的方法參數(shù)實際上是Object類型(泛型擦除)堡称,而且編譯器也不能發(fā)現(xiàn)錯誤。在運行時當參數(shù)類型不是SubClass聲明的類型時艺演,會拋出類型轉(zhuǎn)換異常却紧,因為這時調(diào)用的是橋接方法,而在橋接方法中會進行強制類型轉(zhuǎn)換胎撤,所以才會拋出類型轉(zhuǎn)換異常晓殊。上面的代碼輸出結(jié)果如下:

 
abc123
Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at com.mikan.SubClass.method(SubClass.java:7)
    at com.mikan.BridgeMethodTest.main(BridgeMethodTest.java:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

如果我們在聲明SuperClass類型的變量就指定了泛型類型:

SuperClass<String> superClass = new SubClass();

當然這里類型只能是String,因為SubClass的泛型類型聲明是String類型的伤提,如果指定其他類型巫俺,那么在編譯時就會錯誤,這樣就把類型檢查從運行時提前到了編譯時肿男。這就是泛型的好處介汹。

2、為什么要生成橋接方法

上面看到了編譯器在什么時候會生成橋接方法舶沛,那為什么要生成橋接方法呢嘹承?
在java1.5以前,比如聲明一個集合類型:

List list = new ArrayList();

那么往list中可以添加任何類型的對象如庭,但是在從集合中獲取對象時叹卷,無法確定獲取到的對象是什么具體的類型,所以在1.5的時候引入了泛型坪它,在聲明集合的時候就指定集合中存放的是什么類型的對象:

List<String> list = new ArrayList<String>();

那么在獲取時就不必擔心類型的問題骤竹,因為泛型在編譯時編譯器會檢查往集合中添加的對象的類型是否匹配泛型類型,如果不正確會在編譯時就會發(fā)現(xiàn)錯誤往毡,而不必等到運行時才發(fā)現(xiàn)錯誤瘤载。因為泛型是在1.5引入的,為了向前兼容卖擅,所以會在編譯時去掉泛型(泛型擦除)鸣奔,但是我們還是可以通過反射API來獲取泛型的信息,在編譯時可以通過泛型來保證類型的正確性惩阶,而不必等到運行時才發(fā)現(xiàn)類型不正確挎狸。由于java泛型的擦除特性,如果不生成橋接方法断楷,那么與1.5之前的字節(jié)碼就不兼容了锨匆。如前面的SuperClass中的方法,實際在編譯后的字節(jié)碼如下:

localhost:mikan mikan$ javap -c -v SuperClass.class
Classfile /Users/mikan/Documents/workspace/project/algorithm/target/classes/com/mikan/SuperClass.class
  Last modified 2015-8-7; size 251 bytes
  MD5 checksum 2e2530041f1f83aaf416a2ca3af9b7e3
  Compiled from "SuperClass.java"
public interface com.mikan.SuperClass<T extends java.lang.Object>
  Signature: #7                           // <T:Ljava/lang/Object;>Ljava/lang/Object;
  SourceFile: "SuperClass.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
   #1 = Class              #10            //  com/mikan/SuperClass
   #2 = Class              #11            //  java/lang/Object
   #3 = Utf8               method
   #4 = Utf8               (Ljava/lang/Object;)Ljava/lang/Object;
   #5 = Utf8               Signature
   #6 = Utf8               (TT;)TT;
   #7 = Utf8               <T:Ljava/lang/Object;>Ljava/lang/Object;
   #8 = Utf8               SourceFile
   #9 = Utf8               SuperClass.java
  #10 = Utf8               com/mikan/SuperClass
  #11 = Utf8               java/lang/Object
{
  public abstract T method(T);
    flags: ACC_PUBLIC, ACC_ABSTRACT
    Signature: #6                           // (TT;)TT;
}
localhost:mikan mikan$

通過Signature: #7 // <T:Ljava/lang/Object;>Ljava/lang/Object;可以看到,在編譯完成后泛型實際上就成了Object了恐锣,所以方法實際上成了

public abstract Object method(Object param);

而SubClass中的method方法是:

 public String method(String param) {
        return param;
    }

SubClass實現(xiàn)了SuperClass這個接口茅主,如果不生成橋接方法,那么SubClass就沒有實現(xiàn)接口中聲明的方法土榴,語義就不正確了诀姚,所以編譯器才會自動生成橋接方法,來保證兼容性玷禽。

3赫段、如何通過橋接方法獲取實際的方法

我們在通過反射進行方法調(diào)用時,如果獲取到橋接方法對應的實際的方法呢矢赁?可以查看spring中org.springframework.core.BridgeMethodResolver類的源碼糯笙。實際上是通過判斷方法名、參數(shù)的個數(shù)以及泛型類型參數(shù)來獲取的撩银。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末给涕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子额获,更是在濱河造成了極大的恐慌稠炬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪啡,死亡現(xiàn)場離奇詭異首启,居然都是意外死亡,警方通過查閱死者的電腦和手機撤摸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門毅桃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人准夷,你說我怎么就攤上這事钥飞。” “怎么了衫嵌?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵读宙,是天一觀的道長。 經(jīng)常有香客問我楔绞,道長结闸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任酒朵,我火速辦了婚禮桦锄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔫耽。我一直安慰自己结耀,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著图甜,像睡著了一般碍粥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黑毅,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天嚼摩,我揣著相機與錄音,去河邊找鬼博肋。 笑死低斋,一個胖子當著我的面吹牛蜂厅,可吹牛的內(nèi)容都是我干的匪凡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼掘猿,長吁一口氣:“原來是場噩夢啊……” “哼病游!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稠通,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衬衬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后改橘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滋尉,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年飞主,在試婚紗的時候發(fā)現(xiàn)自己被綠了狮惜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡碌识,死狀恐怖碾篡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筏餐,我是刑警寧澤开泽,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站魁瞪,受9級特大地震影響穆律,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜导俘,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一众旗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趟畏,春花似錦贡歧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律想。三九已至,卻和暖如春绍弟,著一層夾襖步出監(jiān)牢的瞬間技即,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工樟遣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留而叼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓豹悬,卻偏偏與公主長得像葵陵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瞻佛,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容