Effective Java Item 65:偏好interfaces而非反射
整理 Effective Java 書中 Item 65: Prefer interfaces to reflection 心得筆記
主旨
反射(Reflection)是 Java 提供的一項強大功能,讓程式在執行期間也能動態存取類別的建構子、方法、欄位等資訊。不過,反射的代價非常高:
- 沒有編譯期型別檢查
- 需要大量樣板代碼
- 效能遠低於直接呼叫
如果可以,應該優先考慮使用介面或超類別來取代反射的存取方式。
反射的問題點
缺點一:失去型別安全
反射繞過了 Java 的型別系統。錯誤不會在編譯期出現,而是在執行期爆炸,例如:
缺點二:程式碼難讀又冗長
要用反射寫個建構子呼叫就要一堆 try-catch,沒幾行有意義的程式。
缺點三:效能差
反射呼叫一個空參數的方法,在某些機器上會慢 10 倍以上。如果你在迴圈中大量用反射,會讓效能爆炸下降。
範例:反射 + 介面存取的做法
以下是一段用反射動態建立 Set 的範例(根據參數建立 HashSet 或 TreeSet):
✅ 雖然使用了反射,但建立完物件後,透過介面 Set 來操作資料,讓主程式碼邏輯簡單又清晰。
真實應用:多版本支援
反射有一種罕見但合理的用途是跨版本兼容。例如你開發一個工具,需要同時支援 Java 8 與 Java 11,但某些 API 在 Java 11 才有。這時可以:
- 用最舊版本編譯(如 Java 8)
- 執行時動態偵測有沒有 Java 11 的新方法
- 如果有,用反射呼叫;沒有就降級處理
這種做法可以讓你的程式碼在不同 JDK 上都能跑。
小結:什麼時候才該用反射?
| 使用時機 | 建議行為 |
|---|---|
| 不知道類別是誰,但有共同介面/超類別 | ✅ 用反射建立,用介面操作 |
| 需要動態載入外部 plugin/module | ✅ 只用反射處理初始化階段 |
| 工具框架(如 DI、測試工具) | ✅ 有需要但應設計良好封裝反射 |
| 可以用介面、enum、泛型替代的情境 | ❌ 避免反射,保持可讀性與型別安全 |
✅ 結論:能不用反射就不用反射。必要時也僅限在初始化階段,之後一律改用介面來操作物件,這樣才能兼顧彈性與穩定。
Read other posts