Effective Java Item 64:盡量用介面來當作型別
整理 Effective Java 書中 Item 64: Refer to objects by their interfaces 心得筆記
主旨
當在宣告變數、參數或方法回傳值時,應該盡可能使用介面(interface)作為型別,而不是具體類別(class)。
這個做法讓程式碼更具彈性、更容易維護,也符合「針對抽象程式設計」的物件導向原則。
範例:用介面取代具體類別
而不是這樣:
這麼做有什麼好處?
未來如果要改用
HashSet或TreeSet,只要改建構子部分即可:呼叫端不依賴具體實作,因此變動的範圍小、不容易出錯。
劃重點:為什麼這樣比較好?
| 比較項目 | 用介面(Set) | 用類別(LinkedHashSet) |
|---|---|---|
| 替換實作方便 | ✅ 只需改建構子 | ❌ 宣告也要改 |
| 相依性低、耦合低 | ✅ | ❌ 容易被綁死 |
| 支援多型、測試彈性 | ✅ | ❌ 不好 mock 或替換 |
| 風格更一致、簡潔 | ✅ | ❌ 看起來笨重、易出錯 |
注意事項與例外情況
何時可以用類別當型別?
沒有對應介面的值物件
像String、BigDecimal、BigInteger本來就是值物件,用類別沒問題。框架原生就是以類別為核心的
例如 Java IO 的OutputStream或InputStream,用抽象類別來當型別是合理的。你真的需要使用某個實作類別的額外方法
比如你用PriorityQueue是因為它有comparator()這個方法,但Queue介面沒有。這種情況才合理用類別型別。
真實世界範例
假設你寫了一個訂單系統,裡面有以下寫法:
這樣未來如果想改成 LinkedList 或 CopyOnWriteArrayList 就會比較麻煩。建議改為:
這樣在需要時可以輕鬆換實作類別,而不動到其他邏輯。
小結:最佳實務速查表
| 情境 | 建議型別宣告 |
|---|---|
| 一般集合、清單、對映 | 用 List / Set / Map 介面 |
| 資料來源是使用者輸入或程式邏輯決定的實作 | 用介面 |
沒有對應介面的值物件(如 String) | 用類別 |
| 需使用特定實作才有的方法 | 用實作類別(但盡量避免) |
| 測試中想 mock 或注入不同實作 | 用介面,更好切換實作 |
結論:除了建立物件時幾乎都應該用「介面作為型別」,讓你的程式更彈性、可測試又好維護!
Read other posts