jdk1.8新特性:Lambda表示式

devp_yang發表於2019-06-07

Lambda表示式

一、函數語言程式設計思想概述
在數學中,函式就是有輸入量、輸出量的一套計算方案,也就是“拿什麼東西做什麼事情”。相對而言,物件導向過分強調“必須通過物件的形式來做事情”,而函式式思想則儘量忽略物件導向的複雜語法——強調做什麼,而不是以什麼形式做。

二、傳統寫法程式碼示例

public class demo01 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable(){
            public void run(){
                System.out.println("多執行緒任務執行");
            }
        };
        new Thread(runnable).start();
    }
}

說明:

  • Thread類需要Runnable介面作為引數,其中的抽象run方法是用來指定執行緒任務內容的核心;
  • 為了指定run的方法體,不得不需要Runnable介面的實現類;
  • 為了省去定義一個RunnableImpl實現類的麻煩,不得不使用匿名內部類;
  • 必須覆蓋重寫抽象run方法,所以方法名稱、方法引數、方法返回值不得不再寫一遍,且不能寫錯;
  • 而實際上,似乎只有方法體才是關鍵所在

三、上述示例使用Lambda的寫法

public class demo01 {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("多執行緒啟動");
        }).start();
    }
}

上例中,核心程式碼其實只是如下所示的內容:() -> System.out.println("多執行緒任務執行!")

四、Lambda標準格式
Lambda省去物件導向的條條框框,格式由3個部分組成:

  • 一些引數
  • 一個箭頭
  • 一段程式碼

Lambda表示式的標準格式為:(引數型別 引數名稱) -> { 程式碼語句 }
格式說明:

  • 小括號內的語法與傳統方法引數列表一致:無引數則留空;多個引數則用逗號分隔。
  • ->是新引入的語法格式,代表指向動作。
  • 大括號內的語法與傳統方法體要求基本一致

五、練習:使用Lambda標準格式(無參無返回)
給定一個廚子Cooker介面,內含唯一的抽象方法makeFood,且無引數、無返回值。如下:

public interface Cooker {
    void makeFood();
}

在下面的程式碼中,請使用Lambda的標準格式呼叫invokeCook方法,列印輸出“吃飯啦!”字樣:

public class demo01 {
    public static void main(String[] args) {
        invokeCook(() -> {
            System.out.println("吃飯啦!");
        });

    }
    private static void invokeCook(Cooker c){
        c.makeFood();
    }
}

六、Lambda的引數和返回值
需求:使用Lambda表示式對存在陣列中的Person型別的物件資料按照年齡的降序輸出。
傳統寫法:

public class Demo02 {
    public static void main(String[] args) {
      	// 本來年齡亂序的物件陣列
        Person[] array = {
        	new Person("古力娜扎", 19),
        	new Person("迪麗熱巴", 18),
       		new Person("馬爾扎哈", 20) };

      	// 匿名內部類
        Comparator<Person> comp = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o2.getAge() - o1.getAge();
            }
        };
        Arrays.sort(array, comp); // 第二個引數為排序規則,即Comparator介面例項

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

Lambda寫法:

public class demo02 {
    public static void main(String[] args) {
        // 本來年齡亂序的物件陣列
        Person[] array = {
                new Person("古力娜扎", 19),
                new Person("迪麗熱巴", 18),
                new Person("馬爾扎哈", 20) };

        Arrays.sort(array,(Person p1,Person p2) -> {
            return p2.getAge() - p1.getAge();
        });

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

七、練習
給定一個計算器Calculator介面,內含抽象方法calc可以將兩個int數字相加得到和值:

public interface Calculator {
    int calc(int a, int b);
}

程式碼示例:使用Lambda的標準格式呼叫invokeCalc方法,完成120和130的相加計算

public class demo03 {
    public static void main(String[] args) {
        invokeCalc(120,130,(int a,int b) -> {
            return a + b;
        });
    }
    private static void invokeCalc(int a, int b, Calculator calculator) {
        int result = calculator.calc(a, b);
        System.out.println("結果是:" + result);
    }
}

八、Lambda省略格式
Lambda強調的是“做什麼”而不是“怎麼做”,所以凡是可以根據上下文推導得知的資訊,都可以省略。例如上例還可以使用Lambda的省略寫法:

public static void main(String[] args) {
  	invokeCalc(120, 130, (a, b) -> a + b);
}

省略規則
在Lambda標準格式的基礎上,使用省略寫法的規則為:

  1. 小括號內引數的型別可以省略;
  2. 如果小括號內有且僅有一個引數,則小括號可以省略;
  3. 如果大括號內有且僅有一個語句,則無論是否有返回值,都可以省略大括號、return關鍵字及語句分號。
    注意:關於第三點如果省略一個,那麼必須同時都得省略。

九、練習:使用Lambda省略格式
仍然使用前文含有唯一makeFood抽象方法的廚子Cook介面,在下面的程式碼中,請使用Lambda的省略格式呼叫invokeCook方法,列印輸出“吃飯啦!”字樣:

public class demo01 {
    public static void main(String[] args) {
        invokeCook(() -> System.out.println("吃飯啦!")
        );
    }
    private static void invokeCook(Cooker c){
        c.makeFood();
    }
}

十、Lambda的使用前提
Lambda的語法非常簡潔,完全沒有物件導向複雜的束縛。但是使用時有幾個問題需要特別注意:

  1. 使用Lambda必須具有介面,且要求介面中有且僅有一個抽象方法。
    無論是JDK內建的Runnable、Comparator介面還是自定義的介面,只有當介面中的抽象方法存在且唯一時,才可以使用Lambda。
  2. 使用Lambda必須具有上下文推斷。
    也就是方法的引數型別必須為Lambda對應的介面型別,才能使用Lambda作為該介面的例項。

舉例:

 private static void invokeCook(Cooker cook) {
   cook.makeFood();
}

對於invokeCook方法的引數型別必須是介面,這裡Cooker就是一個介面。
備註:有且僅有一個抽象方法的介面,稱為“函式式介面”。)

相關文章