這篇是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的函數式編程時代