一、 多型的底層機制:動態綁定與虛擬方法表(vtable)
在 Java 中,方法呼叫主要分為兩種:
JVM在類別載入與連結階段,會為每個類別在方法區建立一張虛擬方法表:陣列的每個元素都是一個指標,指向該類別具體實作的方法記憶體位址。如果子類別改寫了父類別的方法,子類別vtable對應索引位置的指標就會換成子類別的實作位址,如果沒有改寫,就沿用指向父類別的位址。執行期進行方法呼叫時,JVM只需根據方法的索引偏移量直接跳轉,時間複雜度為 $O(1)$。
二、 轉型陷阱:向上轉型 vs 向下轉型
在型別轉換中,方向決定了程式碼的安全性:
*向上轉型(Upcasting):天然安全
子類別是父類別的特化版本,子類別實體必然具備父類別宣告的所有能力。
Payment payment = new ApplePayPayment();
*向下轉型(Downcasting):未爆彈的起點
如果我們想拿回子類別專屬的特有方法,就必須向下轉型。
Payment payment = new LinePayPayment();
Payment payment = new LinePayPayment();
三、架構解耦實戰:用多型替換龐大的switch/if-else
在業務系統演化時,經常出現這類充斥著型別判斷的反模式代碼。
public class CheckoutService {
public void processPayment(String type, int amount) {
if ("LINE_PAY".equals(type)) {
} else if ("APPLE_PAY".equals(type)) {
} else if ("CREDIT_CARD".equals(type)) {
}
}
}
多型重構:策略模式的雛形透過定義統一的抽象行為契約,將控制權反轉給具體物件。
public interface Payment {
void pay(int amount);
}
public class LinePayPayment implements Payment {
@Override
public void pay(int amount) {
System.out.println("使用 LinePay 扣款:" + amount);
}
}
public class CreditCardPayment implements Payment {
@Override
public void pay(int amount) {
System.out.println("使用信用卡刷卡:" + amount);
}
}
public class OrderService {
public void checkout(Payment payment, int amount) {
payment.pay(amount);
}
}
只需要建立一個全新的JkoPayment類別並實作Payment,原本的OrderService完全不需要改動任何一行代碼。