【譯】Java 橋接方法詳解

Java 橋接方法詳解

Java 中的橋接方法是一種合成方法炸枣,在實現(xiàn)某些 Java 語言特性的時候是很有必要的即横。最為人熟知的例子就是協(xié)變返回值類型和泛型擦除后導致基類方法的參數(shù)與實際調(diào)用的方法參數(shù)類型不一致礁遵。

看一下以下的例子:

public class SampleOne {
    public static class A<T> {
        public T getT() {
            return null;
        }
    }

    public static class  B extends A<String> {
        public String getT() {
            return null;
        }
    }
}

事實上這就是一個協(xié)變返回類型的例子单起,泛型擦除后將會變成類似于下面這樣的代碼段:

public class SampleOne {
    public static class A {
        public Object getT() {
            return null;
        }
    }

    public static class  B extends A {
        public String getT() {
            return null;
        }
    }
}

在將編譯后的字節(jié)碼反編譯后阳谍,類 B 會是這樣子的:

public class SampleOne$B extends SampleOne$A {
public SampleOne$B();
...
public java.lang.String getT();
Code:
0:   aconst_null
1:   areturn
public java.lang.Object getT();
Code:
0:   aload_0
1:   invokevirtual   #2; // 調(diào)用 getT:()Ljava/lang/String;
4:   areturn
}

從上面可以看到蛀柴,有一個新合成的方法 java.lang.Object getT(), 這在源代碼中是沒有出現(xiàn)過的。這個方法就起了一個橋接的作用矫夯,它所做的就是把對自身的調(diào)用委托給方法 jva.lang.String getT()鸽疾。編譯器不得不這么做,因為在 JVM 方法中训貌,返回類型也是方法簽名的一部分制肮,而橋接方法的創(chuàng)建就正好是實現(xiàn)協(xié)變返回值類型的方式。

現(xiàn)在再看一看下面和泛型相關(guān)的例子:

public class SampleTwo {
    public static class A<T> {
        public T getT(T args) {
            return args;
        }
    }

    public static class B extends A<String> {
        public String getT(String args) {
            return args;
        }
    }
}

編譯后類 B 會變成下面這樣子:

public class SampleThree$B extends SampleThree$A{
public SampleThree$B();
...
public java.lang.String getT(java.lang.String);
Code:
0:   aload_1
1:   areturn

public java.lang.Object getT(java.lang.Object);
Code:
0:   aload_0
1:   aload_1
2:   checkcast       #2; //class java/lang/String
5:   invokevirtual   #3; //Method getT:(Ljava/lang/String;)Ljava/lang/String;
8:   areturn
}

這里的橋接方法覆蓋了(override)基類 A 的方法递沪,不僅使用字符串參數(shù)將對自身的調(diào)用委派給基類 A 的方法豺鼻,同時也執(zhí)行了一個到 java.lang.String 的類型轉(zhuǎn)換檢測(#2)。這就意味著如果你運行下面這樣的代碼款慨,忽略編譯器的“未檢”(unchecked)警告儒飒,結(jié)果會是從橋接方法那里拋出異常 ClassCastException

A a = new B();
a.getT(new Object()));

以上例子就是橋接方法最為人熟知的兩種使用場景檩奠,但至少還有一種使用案例桩了,就是橋接方法被用于“改變”基類可見性附帽。考慮以下示例代碼井誉,猜測一下編譯器是否需要創(chuàng)建一個橋接方法:

package samplefour;

public class SampleFour {
    static class A {
        public void foo() {
        }
    }
    public static class C extends A {

    }
    public static class D extends A {
        public void foo() {
        }
    }
}

如果你反編譯 C 類蕉扮,你將會看到有 foo 方法,它覆蓋了基類的方法并把對自身的調(diào)用委托給它(基類的方法):

public class SampleFour$C extends SampleFour$A{
...
public void foo();
Code:
0:   aload_0
1:   invokespecial   #2; //Method SampleFour$A.foo:()V
4:   return

}

編譯器需要這樣的方法颗圣,因為 A 類不是公開的慢显,在 A 類所在包之外是不可見的,但是 C 類是公開的欠啤,它所繼承來的所有方法在所在包之外也都應(yīng)該是可見的荚藻。需要注意的是,D 類不會有橋接方法生成洁段,因為它覆蓋了 foo 方法应狱,因此沒有必要“提升”其可見性。
這種橋接方法似乎是由于這個 bug(在 Java 6 被修復(fù))才引入的祠丝。這意味著在 Java 6 之前是不會生成這樣橋接方法的疾呻,那么 C#foo 就不能夠在它所在包之外使用反射調(diào)用,以致于下面這樣的代碼在 Java 版本小于 1.6 時會報 IllegalAccessException 異常写半。

package samplefive;
...
SampleFour.C.class.getMethod("foo").invoke(new SampleFour.C());
...

不使用反射機制岸蜗,正常調(diào)用的話是起作用的。

可能還有其他使用橋接方法的案例叠蝇,但沒有相關(guān)的資料璃岳。此外,關(guān)于橋接方法也沒有明確的定義悔捶,盡管你可以很容易的猜測出來铃慷,像以上的示例是相當明顯的,但如果有一些規(guī)范把橋接方法說明清楚的話就更好了蜕该。盡管自 Java 5 開始 Method#isBridge() 方法 就是公開的反射 API 了犁柜,橋接的標志也是字節(jié)碼文件格式中的一部分,但 Java 虛擬機和 Java 語言規(guī)范都始終沒有任何關(guān)于橋接方法的確切文檔堂淡,也沒有提供關(guān)于編譯器何時/如何使用橋接方法的任何規(guī)則馋缅。我所能找到的全部引用都是來自這里的“討論區(qū)”


掘金翻譯計劃 是一個翻譯優(yōu)質(zhì)互聯(lián)網(wǎng)技術(shù)文章的社區(qū)绢淀,文章來源為 掘金 上的英文分享文章萤悴。內(nèi)容覆蓋 AndroidiOS更啄、前端稚疹、后端居灯、區(qū)塊鏈祭务、產(chǎn)品内狗、設(shè)計人工智能等領(lǐng)域义锥,想要查看更多優(yōu)質(zhì)譯文請持續(xù)關(guān)注 掘金翻譯計劃柳沙、官方微博知乎專欄拌倍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赂鲤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子柱恤,更是在濱河造成了極大的恐慌数初,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梗顺,死亡現(xiàn)場離奇詭異泡孩,居然都是意外死亡,警方通過查閱死者的電腦和手機粟关,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門击孩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艺玲,“玉大人法绵,你說我怎么就攤上這事徙赢≌硖耄” “怎么了哥倔?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長附迷。 經(jīng)常有香客問我艾猜,道長,這世上最難降的妖魔是什么埠居? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮事期,結(jié)果婚禮上滥壕,老公的妹妹穿的比我還像新娘。我一直安慰自己兽泣,他們只是感情好绎橘,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唠倦,像睡著了一般称鳞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稠鼻,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天冈止,我揣著相機與錄音,去河邊找鬼候齿。 笑死熙暴,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的慌盯。 我是一名探鬼主播周霉,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼亚皂!你這毒婦竟也來了俱箱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤灭必,失蹤者是張志新(化名)和其女友劉穎匠楚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厂财,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡芋簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了璃饱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片与斤。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撩穿,到底是詐尸還是另有隱情磷支,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布食寡,位于F島的核電站雾狈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏抵皱。R本人自食惡果不足惜善榛,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望呻畸。 院中可真熱鬧移盆,春花似錦、人聲如沸伤为。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绞愚。三九已至叙甸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間位衩,已是汗流浹背蚁署。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蚂四,地道東北人光戈。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像遂赠,于是被迫代替她去往敵國和親久妆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

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