Lambda 是 Java 8.0 導入的精簡語法,可以大幅縮減程式碼的長度。
Lambda 只適用於介面的 SAM 方法。
SAM
SAM 全名為 Single Abstract Method (單一抽象方法),意思是指一個介面只擁有單一抽象方法。
請注意,抽象類別也可能只有一個抽象方法,但 SAM 是針對 interface,所以不適用於抽象類別。
如下是 SAM 的代碼。
interface Runnable{
void run();
}
每次在執行新的執行緒,都需要 new runnable() 產生 Runnable 的匿名類別,如下
new Thread(new Runnable(){
@Override
public void run() {
//要執行的工作任務
}
}).start();
像這種只擁有單一抽象方法的介面多不勝數,為什麼會有這麼多無聊的介面呢? 說穿了只是為了多型而以。
上面所說的又跟 Lambda 有什麼關係? Lambda 是 Java8.0 新增的功能,用來簡化 SAM 的用法,不但連匿名類別都不用,連 new 都省略。
正規寫法
在如下的代碼中,正規寫法需產生 Pikachu 類別實作 Land 介面,然後在主程式產生 Pikachu 物件,再由物件調用 walk 方法。
interface Land{
void walk(String s);
}
class Pikachu implements Land{
public void walk(String s){
System.out.println(s);
}
}
public class Main {
public static void main(String[] args) {
os.utf8();
Land p1=new Pikachu();
p1.walk("用跑的");
}
}
Lambda 語法
使用 Lambda 語法可以簡化成如下,連匿名類別都不用
interface Land{
void walk(String s);
}
public class Main {
public static void main(String[] args) {
os.utf8();
Land p1=(s)->{
System.out.println(s);
};
p1.walk("用跑的");
}
}
Runnable 介面
如果是 Runnable 介面,則可簡化成如下
public class Main {
public static void main(String[] args) {
os.utf8();
Runnable r=()->{System.out.println("新執行緒");};
new Thread(r).start();
}
}
傳入 new Thread 的參數,甚至不須宣告為 Runnable 物件,因為系統知道 new Thread 所需要的東西,所以可以直接寫成如下。
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);
}
