多態是面向物件三大特性之一,指「一個介面,多種實現」。本文系統講解多態的滿足條件、表現形式、編譯期與執行期綁定差異、成員存取規則(變數/非靜態方法/靜態方法/子類特有成員),並附記憶體模型圖解。
extends 父類,或介面實作。Parent p = new Child();Animal a = new Dog(); // 父類引用指向子類物件 Animal b = new Fish(); // 同一父類引用可指向不同子類物件 a.cry(); // 執行 Dog.cry() b.cry(); // 執行 Fish.cry()
| 階段 | 綁定依據 | 說明 |
|---|---|---|
| 編譯期 | 引用型別(左邊) | 檢查引用型別是否擁有該成員,無則編譯錯誤 |
| 執行期 | 實際物件型別(右邊) | 動態分派:非靜態方法呼叫子類重寫版本 |
口訣:編譯看左邊,執行看右邊(僅限非靜態方法)。
| 成員類型 | 父子同名時存取結果 | 原因 |
|---|---|---|
| 成員變數(非靜態/靜態) | 父類 | 編譯期靜態綁定,引用型別決定 |
| 非靜態方法 | 子類 | 執行期動態分派,實際物件決定 |
| 靜態方法 | 父類 | 靜態綁定,屬於類不屬於物件 |
| 子類特有成員 | 無法直接存取 | 父類引用不知曉子類擴展成員 |
class Animal {
String name = "動物";
void cry() { System.out.println("動物叫聲"); }
static void breathe() { System.out.println("動物呼吸"); }
}
class Dog extends Animal {
String name = "狗";
void cry() { System.out.println("汪汪"); }
static void breathe() { System.out.println("狗呼吸"); }
void fetch() { System.out.println("撿球"); }
}
public class PolyTest {
public static void main(String[] args) {
Animal a = new Dog();
System.out.println(a.name); // 動物(父類變數)
a.cry(); // 汪汪(子類非靜態方法)
a.breathe(); // 動物呼吸(父類靜態方法)
// a.fetch(); // 編譯錯誤:Animal 無 fetch()
((Dog)a).fetch(); // 強轉後可呼叫子類特有方法
}
}
public 變數,改用 private + getter/setter,防止多態下變數隱藏造成困惑。Animal.breathe()。instanceof 守衛後強轉,或引入訪問者模式/雙分派避免大量 if-else。