多態:同一介面,多種實現。Java 兩大多態:
Parent p = new Child(); (向上轉型)
// 簡化原理圖解
class Animal {
void sound() { System.out.println("動物聲音"); }
void breathe() { System.out.println("呼吸"); }
}
class Dog extends Animal {
@Override void sound() { System.out.println("汪汪"); }
void fetch() { System.out.println("撿球"); }
}
// JVM 內部為每個類建立 vtable (方法表)
// Animal vtable: [sound()->Animal.sound, breathe()->Animal.breathe]
// Dog vtable: [sound()->Dog.sound, breathe()->Animal.breathe] (繼承)
// 執行時:
Animal a = new Dog();
a.sound(); // 1. 取得 a 實際類型 Dog
// 2. 查 Dog vtable 中 sound 入口
// 3. 調用 Dog.sound() → 輸出 "汪汪"
// 注意:a.fetch() 編譯錯誤!Animal 引用不可見子類特有方法
| 維度 | 重載 | 重寫 |
|---|---|---|
| 發生範圍 | 同一類內 | 父子類/介面實現類之間 |
| 方法名 | 相同 | 相同 |
| 參數列表 | 必須不同 (類型/個數/順序) | 必須相同 |
| 返回類型 | 可不同 | 兼容 (子類型/基本型別相同) |
| 拋出異常 | 無限制 | 子類異常 ⊆ 父類異常 |
| 存取修飾符 | 無限制 | 子類 ≥ 父類 (不能縮小) |
| 綁定時機 | 編譯期 (靜態) | 運行期 (動態) |
| 多態類型 | 編譯時多態 | 運行時多態 |
| @Override | 不可用 | 強烈建議加 (編譯器驗證) |
class A {
public String show(D obj) { return "A and D"; }
public String show(A obj) { return "A and A"; }
}
class B extends A {
public String show(B obj) { return "B and B"; } // 重載 (參數不同)
@Override public String show(A obj) { return "B and A"; } // 重寫
}
class C extends B {}
class D extends B {}
// 測試
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); // ①
System.out.println(a1.show(c)); // ②
System.out.println(a1.show(d)); // ③
System.out.println(a2.show(b)); // ④
System.out.println(a2.show(c)); // ⑤
System.out.println(a2.show(d)); // ⑥
System.out.println(b.show(b)); // ⑦
System.out.println(b.show(c)); // ⑧
System.out.println(b.show(d)); // ⑨
| 呼叫 | 輸出 | 解析 |
|---|---|---|
| ① a1.show(b) | A and A | a1 編譯類型 A,b 編譯類型 B。A 中無 show(B),向上找 show(A) → A.show(A) |
| ② a1.show(c) | A and A | 同 ①,C 編譯類型 C,A 無 show(C) → show(A) |
| ③ a1.show(d) | A and D | A 有 show(D) 精確匹配 → A.show(D) |
| ④ a2.show(b) | B and A | a2 編譯類型 A,實際類型 B。A 無 show(B) → 找 show(A)。實際類型 B 重寫了 show(A) → B.show(A) |
| ⑤ a2.show(c) | B and A | 同 ④,C 編譯類型 C,A 無 show(C) → show(A) → B.show(A) |
| ⑥ a2.show(d) | A and D | A 有 show(D) 精確匹配。雖然 a2 實際是 B,但 B 無 show(D),不涉及重寫 → A.show(D) |
| ⑦ b.show(b) | B and B | b 編譯類型 B,有 show(B) 精確匹配 → B.show(B) |
| ⑧ b.show(c) | B and B | b 編譯類型 B,無 show(C)。向上找父類 A 無 show(C)。第三優先級 this.show((super)C) → this.show(B) → B.show(B) |
| ⑨ b.show(d) | A and D | b 編譯類型 B,無 show(D)。父類 A 有 show(D)。B 未重寫 show(D) → A.show(D) |
this.show(O):當前類精確匹配參數類型super.show(O):父類精確匹配this.show((super)O):當前類匹配參數的父類型super.show((super)O):父類匹配參數的父類型關鍵:編譯器根據引用變數編譯時類型的參數類型進行靜態綁定 (選擇重載);運行時根據物件實際類型進行動態綁定 (選擇重寫)。
// 1. 定義介面 (契約)
interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 2. 多種實現
@Component("alipay") class AlipayStrategy implements PaymentStrategy { ... }
@Component("wechat") class WechatStrategy implements PaymentStrategy { ... }
@Component("unionpay") class UnionPayStrategy implements PaymentStrategy { ... }
// 3. 上下文使用多態
@Service
class PaymentService {
private final Map strategies;
// Spring 自動注入所有實現到 Map (key 為 @Component value)
public PaymentService(Map strategies) {
this.strategies = strategies;
}
public void process(String channel, BigDecimal amount) {
PaymentStrategy strategy = strategies.get(channel);
if (strategy == null) throw new IllegalArgumentException("不支援管道: " + channel);
strategy.pay(amount); // 多態調用
}
}
// 4. 新增管道只需新增實現類,零修改既有代碼
parent.field 總是訪問編譯類型的欄位 (隱藏非重寫)