這篇是Effective Java - Prefer lambdas to anonymous classes章節的讀書筆記 本篇的程式碼來自於原書內容
我們把只定義一個函數的介面或是抽象類別 稱為 函數類型 (function types) 而函數類型的實例稱為函數對象(funcitonal objects)
一直以來 我們創建函數對象的主要方法是匿名類
看一下我們以前怎麼用匿名類排序
// Anonymous class instance as a function object - obsolete! Collections.sort(words, new Comparator<String>() { public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } });
sort的第二個參數 是一個有實作Comparator的類別的實例 這樣的寫法等於是在sort需要一個類別的時候 隨手寫給你 當然這個你隨手寫的類別需要實作compare
這種寫法使得匿名類適用於一個著名的設計模式 -策略模式
Comparator就是抽象策略(FlyBehavior) 匿名類就是具體策略(FlyWithWings)
在Java8中 使用單個抽象方法的接口我們稱為函數式接口 而函數式接口可以用lambda表達式來創建這些接口的實例
上例子
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
優美 簡潔 我甚至連s1 s2的類型都不用跟它說 它可以從words推斷
你還可以用比較器的構造方法(Comparator construction method)
Collections.sort(words, comparingInt(String::length));
Java8之後List還支援了sort方法
words.sort(comparingInt(String::length));
以上的例子 我們看得出來lamdba讓我們程式更加簡潔
lambda不只讓你的程式更加簡潔 還更加實用
我們假設我們有個enum 裡面有4個operation
public enum Operation { PLUS, MINUS, TIMES, DIVIDE; // Do the arithmetic operation represented by this constant public double apply(double x, double y) { switch(this) { case PLUS: return x + y; case MINUS: return x - y; case TIMES: return x * y; case DIVIDE: return x / y; } throw new AssertionError("Unknown op: " + this); } }
用法是這樣
Operation.PLUS.apply(2.0, 3.0); // 5
用法簡單 可是這個enum的寫法很醜 如果沒有throw Exception會無法編譯
比較好一點的寫法如下
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;}}, TIMES {public double apply(double x, double y){return x * y;}}, DIVIDE{public double apply(double x, double y){return x / y;}}; public abstract double apply(double x, double y); }
使用方法一樣 但這個寫法對於每一個enum要做的事 都直接定義一個function
這時候就是用lambda的好時機
public enum Operations { PLUS ((x, y) -> x + y), MINUS ((x, y) -> x - y), TIMES ((x, y) -> x * y), DIVIDE((x, y) -> x / y); private final DoubleBinaryOperator op; Operations(DoubleBinaryOperator op) { this.op = op; } public double apply(double x, double y) { return op.applyAsDouble(x, y); } }
這裡的DoubleBinaryOperator是一個函數類型 也就是只有一個函數的介面
@FunctionalInterface public interface DoubleBinaryOperator { /** * Applies this operator to the given operands. * * @param left the first operand * @param right the second operand * @return the operator result */ double applyAsDouble(double left, double right); }
而我們實作這個函數介面 完全不需要匿名類 我們直接給一個符合需求的lambda 也就是輸入兩個double 輸出一個double的函數
1.沒有函數名稱 沒有文檔 所以lambda必須要非常好懂 別人一看就知道在幹嘛 可以一行搞定就一行搞定 不然也不要超過三行 無法做到以上幾點的話 那就乖乖寫一個函式
2.僅限於函數式接口
3.無法使用this來獲得自身對象的引用
1.想創建抽象類的實例
2.想創建有多個抽象方法接口的實例
3.想獲得自身對象的引用(可以利用this引用匿名類的實例)
lambda和匿名類都無法可靠的序列化 你也應該不太有機會會要序列化匿名類或lambda
但如果你真的需要序列化 使用私有靜態嵌套類的實例
lambda是表示小的函數對象的最佳方式 除非必須創建非函數式接口類型的實例 否則不要使用匿名類作為函數對象
lambda表達式的許多特點 也開始了Java的函數式編程時代