Effective Java Item 44:用標準函式介面
整理 Effective Java 書中 Item 44: Use standard functional interfaces 心得筆記
主旨:學會選擇正確的標準函式介面
從 Java 8 開始,因為 lambda 的加入,我們寫 API 的思維也隨之改變。如果你需要傳入一段行為(function object),不再需要額外建立匿名類別,而是可以用 lambda 寫得更簡潔。但要寫出真正好用的 API,選對函式介面更是關鍵——盡可能用 Java 內建的標準函式介面,能讓程式碼更直觀、可讀性更高,也方便 IDE 自動補全與檢查。
點出問題:不該每次都自創介面
假設你想要建立一個可限制最大筆數的快取,會用 LinkedHashMap
來實作,並覆寫 removeEldestEntry()
:
但如果要將這段行為抽成參數傳入 constructor,應該怎麼設計呢?
直覺會想自訂一個像這樣的介面:
這樣雖然可行,但會造成 API 多了不必要的學習成本,而且失去了與現成工具(如 Streams、Collections)整合的優勢。
範例:善用 BiPredicate
就夠了
事實上,上面這個介面可以直接用 Java 內建的 BiPredicate<Map<K,V>, Map.Entry<K,V>>
取代:
你根本不需要額外創建 EldestEntryRemovalFunction
這種客製化介面,只要用標準的 BiPredicate
,程式碼清楚簡潔又具可讀性。
劃重點:掌握六大核心標準函式介面
其實你只要熟悉下面這六種函式介面,就能應付大部分情境:
介面 | 函數原型 | 範例 |
---|---|---|
Predicate<T> | boolean test(T t) | Collection::isEmpty |
Function<T,R> | R apply(T t) | Arrays::asList |
Supplier<T> | T get() | Instant::now |
Consumer<T> | void accept(T t) | System.out::println |
UnaryOperator<T> | T apply(T t) | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add |
此外,還有針對 int
, long
, double
的變型,如 IntPredicate
, LongFunction<R>
,總共 43 種。名稱都很規則,只要記住邏輯,要查詢並不難。
真實世界範例:Comparator 就是自訂介面的好例子
雖然我們鼓勵使用標準介面,但也不是「一刀切」。像是 Comparator<T>
就是自訂介面的好例子,雖然它與 ToIntBiFunction<T, T>
結構相同,但:
- 名稱具說明性
- 有清楚的契約(例如:要實作
compare
,需要滿足自反性、對稱性) - 有許多實用的 default methods(例如:
thenComparing()
)
只要滿足這三個條件之一,就值得自訂介面來提高 API 可用性與可讀性。
小結:能用標準的就不要自創
若要讓 API 更現代、清晰、好維護,選用標準函式介面是很關鍵的一步:
- ✅ 優先使用
java.util.function
的標準介面 - 🔍 自訂介面需具備:好名字、有契約、需要擴充 method
- 📌 使用
@FunctionalInterface
註解以防後續誤用 - 🧼 避免在同一參數位置 overload 多種不同函式介面,會造成使用者混淆