為什麼需要抽象類?本文通過「打印機」經典案例,揭示抽象方法在編譯期強約束子類實現關鍵業務邏輯的核心價值,避免「空實現」導致的運行時隱患。
噴墨打印機與針式打印機共享「開機/關機」邏輯,但「打印」實現截然不同。
class Printer {
void open() { System.out.println("OPEN"); }
void close() { System.out.println("CLOSE"); }
void print() { } // 空實現!子類若忘記重寫,打印功能失效卻不報錯
}
class HPPrinter extends Printer {
void print() { System.out.println("噴墨打印"); }
}
class CanonPrinter extends Printer {
void print() { System.out.println("針式打印"); }
}
編譯運行無錯,但若子類開發者疏忽未重寫 print(),程序靜默失效——這是語義錯誤卻逃過語法檢查。
abstract class Printer {
void open() { System.out.println("OPEN"); }
void close() { System.out.println("CLOSE"); }
abstract void print(); // 強制子類實現,否則編譯失敗
}
現在子類若漏寫 print(),編譯器直接報錯:
Error: CanonPrinter is not abstract and does not override abstract method print() in Printer
「如果一段代碼在語意上是錯誤的(關鍵行為為空),它在語法上也應該是錯誤的。抽象方法正是將語義約束上升為語法約束的機制。」
| 場景 | 推薦 | 理由 |
|---|---|---|
| 有共享實現邏輯 (open/close) | 抽象類 | 避免代碼重複,提供模板方法 |
| 純行為契約、多實現無共享代碼 | 接口 | 支持多實現、解耦更徹底 |
| 需要構造器初始化狀態 | 抽象類 | 接口無構造器 |
| Java 8+ 默認方法 | 接口 | 可提供默認實現,但仍無構造器 |
抽象類的核心價值不在於「不能實例化」,而在於 abstract 方法強制子類在編譯期完成關鍵行為實現,將潛在的運行時缺陷前移至開發期發現,是構建健壯類繼承體系的基石。