λ 為希臘的一個字母, 在物理學的公式符號中, 表示波長的意思.
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(()->{
for(int i=0;i<100;i++){
System.out.printf("%s, %d\n", Thread.currentThread().getName(), i);
try {Thread.sleep(10);} catch (InterruptedException e) {}
}
}).start();
}
}
上面的寫法, 不用匿名類別, 也不用new出物件, 看似好像把SAM當成了一般函數來看待. 此例子是這樣子沒錯, 可以直接當成函數來看. 而且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只有一行 : 可省略() or {}, 當然也可以不省略
另外請注意 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的方法, 如此就可得到不同的計算結果.
不過也不是說只有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);
}