在 Java 語言中,abstract class(抽象類)與 interface(介面)是支撐抽象類型定義的雙引擎,正是它們賦予了 Java 強大的物件導向能力。雖然兩者在語法層面極為相似,甚至常被開發者視為可互換,但盲目選擇往往掩蓋了對問題領域本質的誤解。本文將從語法定義、程式設計慣例、設計哲學三個維度深度剖析兩者差異,為架構決策提供理據。
在物件導向領域,並非所有類別都能描述具體物件。當一個類別缺乏足夠資訊來具現化具體對象時,它就是抽象類型。抽象類型封裝的是問題領域中「看似不同、本質相同」的一組概念——例如圖形編輯器中的「圓形」、「三角形」雖屬性各異,但共同隸屬於「形狀」這一抽象概念。因為抽象概念在現實中無對應實體,所以抽象類型無法被實例化。
抽象類型的核心作用在於型別隱藏:定義一組固定的行為契約,卻允許無限種具體實現。模組依賴固定的抽象而非具體實現,從而實現「對擴充開放、對修改關閉」的開放封閉原則(OCP)。這正是物件導向設計最核心的基石之一。
abstract class Demo {
abstract void method1();
abstract void method2();
// 可包含資料成員、非抽象方法
}
interface Demo {
void method1();
void method2();
// 僅能有 static final 常數,所有方法隱含 abstract
}
語法差異只是表象,設計意圖才是核心。抽象類體現 ISA(是一種)關係,要求父子類在概念本質上一致;介面僅要求實作契約,不涉及本質歸屬。
假設領域模型中有「門」(具備 open/close)與「警報器」(具備 alarm)兩個概念,現需設計具備警報功能的 AlarmDoor。
在 Door 中直接新增 alarm 方法,將兩個獨立概念強行耦合。導致依賴 Door 的模組因 Alarm 變更而受影響,違反介面隔離原則(ISP)。
根據 ISP 原則,應將 open/close 與 alarm 定義在各自的抽象類型中。但 Java 不支援多重繼承,故不可雙抽象類。最佳實踐:
// 門的本質行為 → 抽象類(ISA)
abstract class Door {
abstract void open();
abstract void close();
}
// 警報能力 → 介面(LIKEA)
interface Alarm {
void alarm();
}
// 實作類同時繼承門、實作警報
class AlarmDoor extends Door implements Alarm {
void open() { ... }
void close() { ... }
void alarm() { ... }
}
此設計精準表達:「AlarmDoor 本質上是門,同時具備警報能力」。若領域理解相反(本質是警報器、具備門功能),則反轉抽象類與介面角色。選擇依據永遠建立在對問題領域本質的正確理解之上。
抽象類與介面的選擇,絕非語法偏好,而是領域建模準確度的試金石。抽象類封裝「是什麼」,介面定義「能做什麼」。下次面對抉擇時,請先問自己:「這個實體在概念本質上屬於哪個體系?」答案即指引方向。