2021/03/15

[筆記] Effective Java #44 堅持使用標準的函數介面

Effective Java 3rd 簡體中文版筆記 #44 堅持使用標準的函數介面
在 Java 出現 Lambda 後,API 的實作方式也改變了,像是模板方法模式 (Template method pattern),現在的作法多用靜態工廠或是構造方法來達到同樣的效果。以 LinkedHashMap 為例,在覆寫 removeEldestEntry 方法後,新增新元素後,最早之前的元素會被移除,當作 cache 的機制。
protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}
假設欲使用 Lambda 來設計上述的方法,讓 LinkedHashMap 在生成時傳入該方法,可以自行定義一個新的函數介面,這樣的作法沒有問題。但在 package java.util.Function 中,已經定義大量的標準函數介面,所以在撰寫客製的函數介面前,應優先考慮是否有符合的標準函數介面。EldestEntryRemovalFunction<K,V> 回傳的是 boolean 屬於 predicate,又因傳入的參數有兩個,所以使用 BiPredicate 便能取代。
@FunctionalInterface 
interface EldestEntryRemovalFunction<K,V> {
    boolean remove(Map<K,V> map, Map.Entry<K,V> eldest);
}

BiPredicate<Map<K,V>, Map.Entry<K,V>>
下面為常見的函數介面定義,Bi- 開頭的就代表傳入的參數有兩個,像是 BiConsumerBiFunctionBiPredicate
  • Operator 表示其回傳結果與傳入參數為同一類型的函數
  • Predicate 表示傳入一參數然後回傳為 boolean 的函數
  • Function 表示傳入類型為 T 回傳類型為 R 的函數
  • Supplier 表示沒有傳入參數但回傳一個 T 類型的函數
  • Consumer 表示傳入一個參數但沒有任何回傳
介面方法範例
UnaryOperator T apply(T t) String::toLowerCase
BinaryOperator T apply(T t1, T t2) BigInteger::add
Predicate boolean test(T t) Collection::isEmpty
Function<T,R> R apply(T t) Arrays::asList
Supplier T get() Instant::now
Consumer void accept(T t) System.out::println

回到之前談到的 Comparator<T>,它的 compare 與函數介面 ToIntBiFunction<T,T> 相同,那為什麼要另外定義? 主要原因有幾個,在 API 中,Comparatorcompare 的命名比 applyAsInt 明確許多,Comparator 存在許多 default 方法。最後,在定義客製化的函數介面時,一定要使用 @FunctionalInterface 來進行標記。
@FunctionalInterface
public interface Comparator {
    int compare(T o1, T o2);
}

@FunctionalInterface
public interface ToIntBiFunction {
    int applyAsInt(T t, U u);
}
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2021/03/effective-java-44-functional-interfaces.html

沒有留言:

張貼留言