class Animal {
public void eat() { System.out.println("動物進食"); }
}
class Dog extends Animal {
public void eat() { System.out.println("狗吃狗糧"); } // 覆寫
public void eat(String food) { System.out.println("狗吃" + food); } // 多載
}
Animal a = new Dog();
a.eat(); // 正常輸出:狗吃狗糧
a.eat("骨頭"); // 編譯錯誤:找不到符號
變數 a 的宣告型別為 Animal,編譯器只認得 Animal 類別定義的方法簽名。Animal 類別只有 eat(),沒有 eat(String),因此編譯期直接報錯。
執行時 a 指向 Dog 實例,JVM 會依據實際物件型別尋找方法。但這僅適用於覆寫方法。多載方法屬於不同簽名的獨立方法,不屬於多型機制範疇。
Animal a = new Dog();
┌─────────────┐
a ───►│ Dog 物件 │
│ ├─ eat() │ ◄── 動態綁定:執行 Dog.eat()
│ ├─ eat(String)│ 靜態不可見:Animal 無此簽名
│ └─ Animal 部分│
└─────────────┘
| 概念 | 定義 | 多型支援 |
|---|---|---|
| 覆寫 | 子類重新定義父類相同簽名方法 | ✅ 動態綁定 |
| 多載 | 同類中同名不同參數列表方法 | ❌ 靜態綁定 |
| 向上轉型 | 子類物件賦值給父類引用 | 隱藏子類特有成員 |
if (a instanceof Dog) {
Dog d = (Dog) a;
d.eat("骨頭"); // 可呼叫子類特有方法
}
interface Eater {
void eat();
void eat(String food);
}
class Dog implements Eater { ... }
Eater e = new Dog();
e.eat("骨頭"); // 介面定義完整契約,編譯通過
將 eat(String) 移至父類或介面,確保契約完整性。
多型只對覆寫方法生效,多載方法屬於編譯期靜態解析。 父類引用只能存取父類定義的方法簽名,子類特有的多載方法在編譯期即不可見。設計時應遵循「介面隔離原則」,在父類或介面中宣告完整契約,避免向下轉型破壞封裝。