Java 多態語法 Parent p = new Child(); 看似繞彎路,為何不直接 Child c = new Child();?本文從設計原則、擴充性、執行期綁定三個維度解析多態的核心價值。
「每天到吃飯的點,我們習慣說『去吃飯』,而不是『去吃青椒肉絲蓋澆飯』。」
// ❌ 直接依賴具體類:難以擴充、測試、替換
public class OrderService {
private MySQLRepository repo = new MySQLRepository(); // 綁死 MySQL
public void save(Order o) { repo.save(o); }
}
// ✅ 依賴抽象:彈性替換實作
public class OrderService {
private Repository repo; // 介面/父類
public OrderService(Repository repo) { this.repo = repo; } // 建構子注入
public void save(Order o) { repo.save(o); }
}
// 使用時決定具體實作
new OrderService(new MySQLRepository()); // 生產環境
new OrderService(new MockRepository()); // 單元測試
new OrderService(new PostgreSQLRepository()); // 遷移資料庫
父類引用 讓高層模組不依賴低層具體類,實現開放封閉原則 (OCP):對擴充開放,對修改關閉。
List zoo = Arrays.asList(new Cat(), new Dog(), new Bird());
for (Animal a : zoo) {
a.makeSound(); // 編譯期只知 Animal 有 makeSound()
// 執行期動態分派至 Cat.meow()/Dog.bark()/Bird.tweet()
}
Animal 有 makeSound() 方法。Duck 完全不修改迴圈邏輯,只需加入 List。集合、參數、回傳值統一使用父類/介面型別,可容納任意子類實例:
// 參數多態
public void feed(Animal a) { a.eat(); }
// 集合多態
List shapes = List.of(new Circle(), new Rectangle(), new Triangle());
double totalArea = shapes.stream().mapToDouble(Shape::area).sum();
final/private/靜態方法)。this 物件類型決定方法版本 (不支援雙分派,需 Visitor 模式)。| 寫法 | 靈活性 | 適用場景 |
|---|---|---|
Child c = new Child() | 低:綁定具體類 | 單一確定實作、內部邏輯、DTO |
Parent p = new Child() | 高:依賴抽象 | Service 層、策略模式、插件架構、測試替身、集合處理 |
「父類引用指向子類物件」不是為了炫技,而是為了隔離變化、延遲決策、擁抱擴充。這是物件導向設計從「寫代碼」進化到「設計架構」的分水嶺。