程式碼範例
public class Animal {
public void move() { System.out.println("動物會移動"); }
}
public class Dog extends Animal {
public void move() { System.out.println("狗會跑和走"); }
public void bark() { System.out.println("狗會吠叫"); }
}
public static void main(String[] args) {
Animal a = new Animal();
Animal b = new Dog(); // 向上轉型
Dog c = new Dog();
a.move(); // 輸出:動物會移動
b.move(); // 輸出:狗會跑和走(動態綁定)
c.bark(); // 正常編譯執行
b.bark(); // 編譯錯誤:Animal 無 bark 方法
}
核心原理
| 引用變數 | 編譯期類型 | 執行期實際類型 | 可呼叫方法 |
| a | Animal | Animal | Animal 所有方法 |
| b | Animal | Dog | 僅 Animal 宣告的方法(重寫版由 Dog 實作) |
| c | Dog | Dog | Dog 所有方法 |
關鍵結論
- 編譯看左邊,執行看右邊:編譯器依引用類型檢查方法是否存在;JVM 執行時依實際物件類型分派方法。
- 向上轉型(
Animal b = new Dog())獲得多態彈性,但犧牲子類專有方法(如 bark())。
- 若需呼叫子類專有方法,須向下轉型(
((Dog)b).bark()),並建議先 instanceof 判斷。
實務建議
- 宣告型別盡量用父類/介面(
List list = new ArrayList()),降低耦合
- 避免濫用向下轉型,違反開閉原則,應改用多態設計
- 理解「靜態綁定」(重載、private、static、final)與「動態綁定」(重寫)的分野