Lambda 是 Java 8.0 導入的精簡語法,可以大幅縮減程式碼的長度。Lambda 只適用於介面的 SAM 方法。
SAM
SAM 全名為 Single Abstract Method (單一抽象方法),意思是指一個介面只有一個抽象方法,如下所示。
interface Runnable{
void run();
}
像這種只有一個抽象方法的介面多不勝數,為什麼 Java 會有這麼多無聊的介面呢? 說穿了只是為了多型而以。
SAM 只針對 interface,不適用於抽象類別。
正規寫法
正規寫法需先產生一個實作 Runnable 的類別,然後再由此類別產生物件,再將此物件塞入 Thread 裏,如下所示。
匿名類別寫法
由 new Runnable(){} 並實作 run 方法, 產生 Runnable 的匿名類別及匿名物件,如下所示。
Lambda 寫法
使用 Lambda 語法不但連匿名類別都不用,甚至連 new 都可省略。
傳入 new Thread 的參數,沒有宣告為 Runnable 物件,而且連函數名稱也沒有,這是因為系統知道 new Thread 所需要的東西就是 Runnable 物件。
這種寫法類似在 new Thread 裏塞入一個函數,等於是把介面當成函數來看待,稱為 functional interface (函數式介面)。
行為 Behavior
底下的代碼就是函數式介面,但 Java 又給了一個特別名稱,叫「行為」(Behavior)。
() -> {
System.out.println("Lambda");
}
Lambda 不會產生類別
使用 Lambda 不會產生類別,從下面的程式碼中可以驗証得知。使用 Lambda 的片段不可以使用 this.getClass(),但使用匿名類別的片段,可以使用 this.getClass() 取得正確的類別名稱。
Lambda 表示式
Lambda 表示式可以寫成如下
(input) -> {body};
input 及 body 有著多種不同的寫法,請記得底下的說明。
只有一個,可有可無
input 只有一個參數,() 可有可無。
body 只有一行,{} 可有可無。
無或多個,需有 () {}
input 無或多個參數,都需要 ()。
body無或多行,都需要 {}。
只有一行返回值
第一種是使用 {return xxx;},一定要有{}。
第二種是不用 {} 也不需 return,直接寫返回值。
input 參數宣告
input 裏的參數不用宣告型別,也可以雞婆的宣告型別,但宣告型別就一定要加 ()
行為參數化 Behavior Parameterization
說的白話一點,就是函數的參數,可以是另一個函數。
這其實是 C 程式語言一直想解決的一個問題:能不能把「要執行什麼」也當成參數傳給另一個函數?C 語言最後透過函數指標(Function Pointer)實現了這個概念,讓我們可以把一個函數傳進另一個函數,讓它決定「接下來要執行什麼」。
到了 Java,則透過函數式介面(Functional Interface)與 Lambda 表達式,讓這件事情變得更加簡潔、直觀。
將「行為」當成方法的參數傳入,其核心概念是讓方法可以接收另一個方法。Lambda 表達式可以將一段行為傳入方法,使方法的參數可以代表不同的行為,這種設計方式稱為「行為參數化(Behavior Parameterization)」。
底下以 Predicate 介面進行說明。Predicate 是一個需實作 test() 方法的介面,只會回傳 true/false 二個結果。
傳統寫法
傳統的寫法需先產生 MyPredicate 類別,再於主程式產生 a 物件,完整代碼如下
匿名類別
使用匿名類別,可以省略 MyPredicate 的製作。
Lambda 寫法
使用 Lmabda,連實作方法的代碼都可以省略
傳入物件
設定一個 filter 方法,比對 List 裏的所有項目是否大於30,可以將 Predicate c 物件傳入 filter 方法中。
傳入行為
傳入 filter 方法的參數,不再是物件,而是行為(函數)
變型應用
先定義一個 MathOperator 介面,裏面只有一個 operate 抽象方法,然後使用 Lambda 產生 add、sub、multi、div 四個函數式介面,最後傳入 calculate 方法的參數中,如此就可得到不同的計算結果。這等於是把函數傳給 calculate 方法中的參數。
public class JavaApp{
public static void main(String[] args) {
MathOperator add = (int x, int y) -> x+y;
MathOperator sub = (x, y) -> x-y;
MathOperator multi = (x, y) -> {return x*y;};
MathOperator div = (x, y) -> x/y;
int x=100, y=10;
System.out.printf("相加 : %d\n",calculate(x, y, add));
System.out.printf("相減 : %d\n",calculate(x, y, sub));
System.out.printf("相乘 : %d\n",calculate(x, y, multi));
System.out.printf("相除 : %d\n",calculate(x, y, div));
MathOperator pow = new MathOperator(){
@Override
public int operate(int x, int y) {
return (int)(Math.pow(x, y));
}
};
System.out.printf("冥次方 : %d\n",calculate(2, 10, pow));
}
private static int calculate(int a, int b, MathOperator operator){
return operator.operate(a, b);
}
}
interface MathOperator{
int operate(int x, int y);
}
