2021/03/12

[筆記] Effective Java #42 Lambda 優先於匿名類

Effective Java 3rd 簡體中文版筆記 #42 Lambda 優先於匿名類
將只有單個方法需要實作的介面 (interface) 或是抽象類 (abstract class) 稱作函數類型 (function type),上述的實作者稱作函數對象 (function object),生成函數對象的方式大多是匿名類 (Anonymous class),詳見 [#24]。以 Collections.sort 程式碼為例,CustomComparator 以匿名類方式傳入 sort
// Anonymous class
Collections.sort(words, new CustomComparator() {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
});
匿名類在 OOP 的設計模式中,屬於策略模式 (strategy pattern)Comparator 表示一種抽象策略 (abstract strategy),而匿名類則是具體策略 (concrete strategy)。在 Java 8 提出一個想法,讓這種只有一個抽象方法需要實作的介面,擁有特殊對待,它們也被稱作函數介面 (functional interface),Java 允許使用 Lambda 表示式來取代匿名類。
在 Lambda 表示式中,沒有包含任何的類型,編譯器會自行推導。以下面程式碼為例,若是 words 使用 List 而非 List<String>,那麼就會產生編譯錯誤。接著再進一步使用 Comparator 構造方法 (construction method) 來簡化,List 支援 sort 方法,就直接使用 words 來調用。
// Lambda
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
// more simply
Collections.sort(words, Comparator.comparingInt(String::length));
words.sort(Comparator.comparingInt(String::length));
在 enum 中定義抽象方法,讓不同的 Operation 各自實作 apply
public enum Operation {
    PLUS("+")  {
        public double apply(double x, double y) { return x + y; }
    },
    MINUS("-") {
        public double apply(double x, double y) { return x - y; }
    };
    
    private final String symbol;
    Operation(String symbol) { this.symbol = symbol; }
    public abstract double apply(double x, double y);
}
當函數介面出現後,想像成將 apply 方法以 Lambda 方式傳入 enum constructor 中,這時需要找一個 @FunctionalInterface,它定義的抽象方法為傳入兩個 double 參數,回傳結果一 doubleDoubleBinaryOperator 完全符合以上條件,程式碼改寫如下。
@FunctionalInterface
public interface DoubleBinaryOperator {
    double applyAsDouble(double left, double right);
}
public enum Operation {
    PLUS("+", (x, y) -> x + y),
    MINUS("-", (x, y) -> x - y);

    private final String symbol;
    private final DoubleBinaryOperator op;
    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }
    public double apply(double x, double y) {
        return op.applyAsDouble(x, y);
    }
}
Lambda 沒有方法名稱也沒有文件,並不適用於全部情況,Lambda 表示式以一至三行較理想,太長或難以閱讀的 Lambda 還不如完整的方法,仍然有些工作是 Lambda 無法完成的,需要使用匿名類來完成。
  • Lambda 限於函數介面,若要生成抽象類,應使用匿名類。
  • 匿名類才可以實作擁有多個抽象方法的介面或抽象類。
  • Lambda 無法引用自身,this 在 Lambda 內是指外圍類,在匿名類就是指匿名類本身。
Lambda 及匿名類會增加序列化及反序列化的困難度,盡可能不要序列化它們。

轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2021/03/effective-java-42-lambda.html

沒有留言:

張貼留言