Java Lambda

      在〈Java Lambda〉中尚無留言

λ 為希臘的一個字母,在物理學的公式符號中,表示波長的意思。

  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);
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *