Effective Java Item 41:用標記介面定義類型
整理 Effective Java 書中 Item 41: Use marker interfaces to define types 心得筆記
主旨
標記介面(Marker Interface)是沒有任何方法的介面,只是單純用來代表一個「型別」。雖然 Java 也可以用標註(annotation)來達到類似效果,但在要表示「某類型具備某種能力」時,還是推薦使用標記介面,因為它可以帶來更強的型別安全與語意明確性。
點出問題
有些時候,我們希望標示某個類別具備某種能力,例如:
- 可以被序列化(Serializable)
- 支援克隆(Cloneable)
- 是 Thread-safe 的(假設你自訂一個介面 ThreadSafe)
這種情況,很多人會選擇用 @interface 自訂註解:
然後在類別上加上註解:
乍看之下沒問題,但這樣做其實缺少了型別檢查與語意上的一致性。
範例:標記介面的優勢
來看個實際的例子:
假設有個方法只接受 thread-safe 的實作:
這時候,如果你傳入一個沒有實作 ThreadSafe 的物件,編譯器就會報錯,這就是介面帶來的型別保護。
反觀使用 @ThreadSafe 標註的寫法,這類檢查只能靠 runtime 的反射判斷,而且失去編譯時的幫助。
劃重點
- 標記介面是介面,但不定義任何方法
- 可作為方法參數的型別,帶來編譯期型別檢查
- 清楚地表達一個類別「是」某種型別(例如
Serializable) - 比註解更適合用來表示「型別能力」
- 不適合跨層架構使用時,可考慮使用註解取代(例如框架掃描註解)
真實世界範例
JDK 本身就是用標記介面的最佳範例,像是:
JVM 內部會根據這些介面做特別處理,例如序列化只能針對實作 Serializable 的類別進行。
你也可以在專案中自訂:
讓系統能夠針對實作 Auditable 的物件,自動記錄資料修改紀錄或存取歷史。
小結
當需要讓某個類別具備某種語意特性時(例如可序列化、支援安全併發),比起使用註解,定義一個空介面(標記介面)通常是更好的做法。它可以讓 API 在編譯時就能受益於型別檢查,也讓使用者的意圖更清楚。除非需要的是跨層的註解掃描或 metadata 設定,否則盡量避免濫用註解來標示型別語意。
Read other posts