【譯】Java 橋接方法詳解

Java 橋接方法詳解

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

看一下以下的例子:

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;
        }
    }
}

事實(shí)上這就是一個(gè)協(xié)變返回類(lèi)型的例子,泛型擦除后將會(huì)變成類(lèi)似于下面這樣的代碼段:

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

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

在將編譯后的字節(jié)碼反編譯后,類(lèi) B 會(huì)是這樣子的:

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
}

從上面可以看到,有一個(gè)新合成的方法 java.lang.Object getT(), 這在源代碼中是沒(méi)有出現(xiàn)過(guò)的。這個(gè)方法就起了一個(gè)橋接的作用,它所做的就是把對(duì)自身的調(diào)用委托給方法 jva.lang.String getT()。編譯器不得不這么做,因?yàn)樵?JVM 方法中,返回類(lèi)型也是方法簽名的一部分,而橋接方法的創(chuàng)建就正好是實(shí)現(xiàn)協(xié)變返回值類(lèi)型的方式。

現(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;
        }
    }
}

編譯后類(lèi) B 會(huì)變成下面這樣子:

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)基類(lèi) A 的方法,不僅使用字符串參數(shù)將對(duì)自身的調(diào)用委派給基類(lèi) A 的方法,同時(shí)也執(zhí)行了一個(gè)到 java.lang.String 的類(lèi)型轉(zhuǎn)換檢測(cè)(#2)。這就意味著如果你運(yùn)行下面這樣的代碼,忽略編譯器的“未檢”(unchecked)警告,結(jié)果會(huì)是從橋接方法那里拋出異常 ClassCastException。

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

以上例子就是橋接方法最為人熟知的兩種使用場(chǎng)景,但至少還有一種使用案例,就是橋接方法被用于“改變”基類(lèi)可見(jiàn)性。考慮以下示例代碼,猜測(cè)一下編譯器是否需要?jiǎng)?chuàng)建一個(gè)橋接方法:

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 類(lèi),你將會(huì)看到有 foo 方法,它覆蓋了基類(lèi)的方法并把對(duì)自身的調(diào)用委托給它(基類(lèi)的方法):

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

}

編譯器需要這樣的方法,因?yàn)?A 類(lèi)不是公開(kāi)的,在 A 類(lèi)所在包之外是不可見(jiàn)的,但是 C 類(lèi)是公開(kāi)的,它所繼承來(lái)的所有方法在所在包之外也都應(yīng)該是可見(jiàn)的。需要注意的是,D 類(lèi)不會(huì)有橋接方法生成,因?yàn)樗采w了 foo 方法,因此沒(méi)有必要“提升”其可見(jiàn)性。
這種橋接方法似乎是由于這個(gè) bug(在 Java 6 被修復(fù))才引入的。這意味著在 Java 6 之前是不會(huì)生成這樣橋接方法的,那么 C#foo 就不能夠在它所在包之外使用反射調(diào)用,以致于下面這樣的代碼在 Java 版本小于 1.6 時(shí)會(huì)報(bào) IllegalAccessException 異常。

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

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

可能還有其他使用橋接方法的案例,但沒(méi)有相關(guān)的資料。此外,關(guān)于橋接方法也沒(méi)有明確的定義,盡管你可以很容易的猜測(cè)出來(lái),像以上的示例是相當(dāng)明顯的,但如果有一些規(guī)范把橋接方法說(shuō)明清楚的話就更好了。盡管自 Java 5 開(kāi)始 Method#isBridge() 方法 就是公開(kāi)的反射 API 了,橋接的標(biāo)志也是字節(jié)碼文件格式中的一部分,但 Java 虛擬機(jī)和 Java 語(yǔ)言規(guī)范都始終沒(méi)有任何關(guān)于橋接方法的確切文檔,也沒(méi)有提供關(guān)于編譯器何時(shí)/如何使用橋接方法的任何規(guī)則。我所能找到的全部引用都是來(lái)自這里的“討論區(qū)”。


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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容