λ 為希臘的一個字母,在物理學的公式符號中,表示波長的意思。
SAM
SAM 全名為 Single Abstract Method (單一抽象方法),意思是指一個介面只擁有單一個抽象方法,比如下面的介面。
interface Runnable{ void run(); }
每次在執行新的執行緒,都需要 new runnable() 產生 Runnable 的匿名類別,如下
new Thread(new Runnable(){ @Override public void run() { //要執行的工作任務 } }).start();
像這種只擁有單一抽象方法的介面多不勝數,為什麼會有這麼多無聊的介面呢? 說穿了只是為了多型而以。
上面所說的又跟 Lambda 有什麼關係? Lambda 是 Java8.0 新增的功能,用來簡化 SAM 的用法,不但連匿名類別都不用,連new都省略。
連匿名類別都不用
Lambda 可簡化成如下
package ja02;
public class JA02 {
public static void main(String[] args) {
new Thread(
()->{
//要執行的任務
}
).start();
}
}
上面的寫法不用匿名類別,也不用 new 出物件,等於是把介面當成函數來看待。Java8.0 SAM 稱為 functional interface (介面函數化)。不過 Lambda 的功能不僅如此,請看後續說明。
驗証真的連類別都沒有
使用 Lambda 後,真的連類別都沒產生嗎? 從下面的程式碼中可以得知,使用 Lambda 的片段不可以使用 this.getClass(),但使用匿名類別的片段,可以使用 this.getClass() 取得正確的類別名稱。
package ja02; public class JA02 { public static void main(String[] args) { new Thread(()->{ String threadName=Thread.currentThread().getName(); for(int i=0;i<100;i++){ System.out.printf("%s, %s, %d\n", "沒有類別", threadName, i); try {Thread.sleep(10);} catch (InterruptedException ex) {} } }).start(); new Thread(new Runnable(){ public void run(){ String threadName=Thread.currentThread().getName(); for (int i=0;i<100;i++){ System.out.printf("%s, %s, %d\n", this.getClass(), threadName, i); try {Thread.sleep(10);} catch (InterruptedException ex) {} } } }).start(); } } 結果如下 沒有類別, Thread-0, 0 class ja02.JA02$1, Thread-1, 0 沒有類別, Thread-0, 1 class ja02.JA02$1, Thread-1, 1 沒有類別, Thread-0, 2 class ja02.JA02$1, Thread-1, 2
Lambda表示式
Lambda 表示式可以寫成如下
input -> body
input 及 body 有著多種不同的寫法,請記得底下的說明。
input 只有一個可以省略 (),body 只有一行可以省略{},當然也可以不省略
另外請注意 Land a1= x ->{}; x可以宣告型態,也可以不宣告型態。若有宣告型態,就一定要加()
public class JavaApp{ public static void main(String[] args) { Land a1=x->System.out.printf("speed : %d\n", x); Land a2=(x)->System.out.printf("speed : %d\n", x); Land a3=x->{System.out.printf("speed : %d\n", x);}; Land a4=(x)->{System.out.printf("speed : %d\n", x);}; Land a5=(int x)->{}; a1.setSpeed(100); } } interface Land{ void setSpeed(int speed); }
input 無或多個時都需要 (),body無或多行都需要 {}
public class JavaApp{
public static void main(String[] args) {
Land l=()->{};
l.setSpeed();
}
}
interface Land{
void setSpeed();
}
public class JavaApp{ public static void main(String[] args) { Land l=(x, y)->{ System.out.println("setup speed"); return x+y; }; int speed=l.setSpeed(10,20); } } interface Land{ int setSpeed(int x, int y); }
body 只有一行返回值
這種最機車,有二種寫法。第一種是 body 使用 {return xxx;},第二種是無 {} 也沒 return,直接寫返回值即可。
public class JavaApplication56 {
public static void main(String[] args) {
Land a1=()->{return 10;}; //第一種
Land a2=()-> 10; //第二種
System.out.printf("speed : %d\n",l.setSpeed());
}
}
interface Land{
int setSpeed();
}
行為參數化
Todo
介面函數變成方法參數
這是一個非常強大的功能,請看如下程式碼,先定義一個 MathOperator 介面,裏面只有一個 operate 抽象方法。
再使用 Lambda 產生 addOperator,subOperator,multiPoerator,divOperator 四個介面函數(其實也可以看成是物件)。再傳入 calculate 的方法,如此就可得到不同的計算結果。這等於是把函數傳給 calculate 方法中的參數。
不過也不是說只有 Lambda 才可以作到此功能,傳統的方式也可以。如下藍色部份,就是使用匿名類別並傳入 powOperator 這個物件就可以達成,只是寫的比較長一點而以。
public class JavaApp{
public static void main(String[] args) {
MathOperator addOperator=(int x, int y)->x+y;
MathOperator subOperator=(x, y)->x-y;
MathOperator multiOperator=(x, y)->{return x*y;};
MathOperator divOperator=(x, y)->x/y;
int x=100, y=10;
System.out.printf("相加 : %d\n",calculate(x, y, addOperator));
System.out.printf("相減 : %d\n",calculate(x, y, subOperator));
System.out.printf("相乘 : %d\n",calculate(x, y, multiOperator));
System.out.printf("相除 : %d\n",calculate(x, y, divOperator));
MathOperator powOperator=new MathOperator(){
@Override
public int operate(int x, int y) {
return (int)(Math.pow(x, y));
}
};
System.out.printf("冥次方 : %d\n",calculate(2, 10, powOperator));
}
private static int calculate(int a, int b, MathOperator operator){
return operator.operate(a, b);
}
}
interface MathOperator{
int operate(int x, int y);
}