Java 僅支援單繼承(class Son extends FatherA, FatherB 非法),為彌補此限制,引入「介面」機制:一個類可實現多個介面(implements A, B, C)。本文從設計模式角度,深度剖析介面與抽象類的異同、定位與組合策略。
| 維度 | 抽象類 | 介面 | 核心啟示 |
|---|---|---|---|
| 方法實現 | 可提供部分具體實現 | JDK 8 前全為抽象;JDK 8+ 支援 default/static 方法 | 抽象類適合「模板方法模式」共享邏輯 |
| 繼承關係 | 單繼承,構成「is-a」層級結構 | 多實現,構成「like-a」混合型別 | 介面是定義型別契約的理想工具 |
| 擴展影響 | 新增具體方法,所有子類自動獲得 | 新增方法破壞現有實現(除非 default 方法) | 抽象類演化成本低,介面需謹慎變更 |
| 成員變數 | 可有實例/靜態變數、建構子 | 僅能有 static final 常數 | 抽象類可封裝狀態,介面純行為契約 |
結合兩者優勢的標準組合策略:
// 1. 介面定義型別契約(最頂層)
public interface Listener {
void onEvent(Event e);
void onError(Error e);
void onComplete();
}
// 2. 抽象類提供預設實現(中間層)
public abstract class AbstractListener implements Listener {
@Override public void onEvent(Event e) { /* 預設空實現 */ }
@Override public void onError(Error e) { /* 預設記錄日誌 */ }
@Override public void onComplete() { /* 預設清理資源 */ }
}
// 3. 具體類靈活選擇(實現層)
public class MyListener extends AbstractListener {
@Override public void onEvent(Event e) { /* 僅關心事件處理 */ }
}
// 或直接實現介面(需實現所有方法)
public class SimpleListener implements Listener { /* 全部實現 */ }
JDK 大量採用此命名慣例:AbstractList、AbstractMap、AbstractExecutorService 等。
// 變數宣告用介面/抽象類型
List list = new ArrayList<>();
Map cache = new ConcurrentHashMap<>();
Runnable task = () -> System.out.println("執行任務");
// 方法參數/回傳用抽象型別
public void process(List data) { ... }
public Map loadCache() { ... }
// 具體類型洩漏到宣告層
ArrayList list = new ArrayList<>(); // 鎖定實現
HashMap cache = new HashMap<>(); // 難以替換為 ConcurrentHashMap
UserRepository、PaymentService)AbstractController、BaseEntity)Serializable、Comparable、Closeable)誤區 1:「介面不能有實現,所以抽象類更強」
→ JDK 8
default方法打破此限制,但語意不同:介面實現是「預設行為」,抽象類實現是「共享邏輯」。
誤區 2:「所有類都要有介面」
→ 僅需對「需多態替換」「跨模組依賴」「需 Mock 測試」的類抽取介面。DTO、Entity、工具類無需介面。
誤區 3:「繼承抽象類就不能實現介面」
→ Java 支援「單繼承 + 多實現」:
class A extends Base implements I1, I2。
介面定義「做什麼」(契約),抽象類提供「怎麼做」(骨架)。優秀的架構分層為:介面(頂層契約) → 抽象類(中間骨架) → 具體類(葉子實現)。理解此層次,才是真正掌握面向物件設計精髓的起點。