Java 桥接方法(Bridge Method)
目錄
- 重寫方法的返回類型是其父類返回類型的子類型
- 重寫泛型方法生成橋接
什么是「橋接方法」,下面來從兩個例子中體會一下。
重寫方法的返回類型是其父類返回類型的子類型
public class Merchant {public Number actionPrice(double price) {return price * 0.8;} }public class NaiveMerchant extends Merchant {@Overridepublic Double actionPrice(double price) {return 0.9 * price;}public static void main(String[] args) {Merchant merchant = new NaiveMerchant();// price 必須定義成 Number 類型 Number price = merchant.actionPrice(40);System.out.println(price);} } javac Merchant.java NaiveMerchant.java# 反編譯字節碼 javap -v -c NaiveMerchant查看 NaiveMerchant.class 反編譯后字節碼:
public java.lang.Double actionPrice(double);descriptor: (D)Ljava/lang/Double;flags: ACC_PUBLICCode:stack=4, locals=3, args_size=20: dload_11: ldc2_w #2 // double 0.9d4: dmul5: invokestatic #4 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;8: areturnLineNumberTable:line 13: 0// 橋接方法 public java.lang.Number actionPrice(double);descriptor: (D)Ljava/lang/Number;flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETICCode:stack=3, locals=3, args_size=20: aload_01: dload_12: invokevirtual #12 // Method actionPrice:(D)Ljava/lang/Double;5: areturnLineNumberTable:line 9: 0父類 Merchant 的 actionPrice 的返回值是 Number 類型,子類NaiveMerchant 重寫 actionPrice 返回的值類似是 Double 類型,對于 Java 語言是重寫的,但對于 Java 虛擬機解析來說卻不是重寫的,只有當兩個方法的參數類型以及返回類型一致時,Java 虛擬機才會判定為重寫,為了保持重寫的語義,Java 編譯器會在 NaiveMerchant 的字節碼文件中自動生成一個橋接方法來保證重寫語義:
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC ACC_BRIDGE:表示這個一個橋接方法 ACC_SYNTHETIC:表示這個方法由編譯器自動生成 public java.lang.Number actionPrice(double);翻譯橋接方法的字節碼:
public Number actionPrice(double price) {return this.actionPrice(price); }這個橋接方法會去調用 NaiveMerchant 本身重寫的方法。
編譯器通過插入橋接方法來保證重寫的語義,從而 Java 虛擬機通過方法描述符(由方法的參數類型和返回類型構成)定位到具體方法,執行方法調用:
Merchant merchant = new NaiveMerchant();// 這里實際上調用的則是橋接方法 Number price = merchant.actionPrice(40);重寫泛型方法生成橋接
子類在繼承父類的一個泛型方法、或子類實現一個接口的泛型方法,編譯器會在子類的 class 文件中自動生成橋接方法。
interface Customer {String purchase(); }class VIP implements Customer {@Overridepublic String purchase() {return "VIP First !";} }class NOT_VIP implements Customer {@Overridepublic String purchase() {return "VIP First !";} }abstract class MerchantOther<T extends Customer> {public double actionPrice(double price, T customer) {return price * 0.08;} }class VIPOnlyMerchant extends MerchantOther<VIP> {@Overridepublic double actionPrice(double price, VIP customer) {return price * 0.07;} }public class MethodFind {public static void main(String[] args) {} }反編譯父類 MerchantOther 的字節碼文件:
泛型 T 被換成了 Customer,方法簽名是:
再看 VIPOnlyMerchant.class 的字節碼:
子類的字節碼中有一個編譯器自動生成的橋接方法,這個橋接方法翻譯成 Java 代碼就是:
橋接方法調用了子類重寫的泛型方法,執行下面的代碼:
public static void main(String[] args) {MerchantOther merchantOther = new VIPOnlyMerchant();// 調用實際的方法merchantOther.actionPrice(80, new VIP());// 調用的是橋接方法,出現 java.lang.ClassCastException 的異常merchantOther.actionPrice(90, new NOT_VIP());}由于泛型擦除,父類 MerchantOther 的參數實際上是 Customer 類型,為了保證重寫的語義,兼容 1.5(泛型是在 1.5 引入的) 之前的字節碼文件,所以生成橋接方法。new NOT_VIP() 傳入 Customer 類型的參數,編譯器也不會發現錯誤。運行時調用了橋接方法,橋接方法中使用 VIP 進行強制類型轉換,當參數類型不是 VIP 時,就會拋出類型轉換異常。
參考:Effects of Type Erasure and Bridge Methods
Java Bridge Method 詳解
Java中什么是bridge method(橋接方法)
總結
以上是生活随笔為你收集整理的Java 桥接方法(Bridge Method)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 羽毛球之混双战术要点
- 下一篇: Windows10玩转Linux子系统(