Java 8——Lambda表達式
本篇文章不介紹Lambda的前世今生,這裏只對Lambda表達的應用做詳細了解。以及與內部類的差異點。
Lambda表達式
Lambda表達式是Java SE 8版本中引入的新的語法糖。將功能看做方法參數,將代碼看做數據。
Lambda表達式語法:
LambdaParameters -> LambdaBody
Lambda表達式分為三部分:
- 參數列表
- ->符號
- 函數體
如:
() -> {} // 無參,返回結果為空 (x) -> System.out.println(x); // 帶有一個參數 (Thread t) -> { t.start(); } // 帶有一個申明參數 (int x, int y) -> x + y; // 帶有兩個申明參數,一個方法參數; (int x, int y) -> return x +y; // 帶有兩個申明參數,一個方法參數; (x, y) -> return x +y; // 帶有兩個申明參數,一個方法參數;
總體上表現如上形式,是Java中一種新的風格的表達式,和普通的表達的確有很大風格上的差異性。
這種風格的語法優勢:
語法特點決定了語句的簡潔緊湊,通過減少申明類型、return、單行語句去括號等等從而變得更精簡;
具有更強的表達能力,減少冗余的代碼,從而更關註真正的功能語句,語義更精確清晰;
函數式接口
已經對lambda表達式定義好了,但是如何在兼容面向對象的Java體系中使用。在面向對象體系中,一切皆是對象,怎樣將這種表達式作為對象去使用,從而引入函數使接口:只有一個抽象方法的接口,代表著單功能的契約。
為了區分函數式接口和只包含一個抽象方法的普通接口,需要使用@FunctionalInterface註解標註接口,這樣編譯器就能將其作為函數式接口處理。
有了函數式接口就可以很方便的使用lambda表達式,使用Java來進行函數式編程。
Function<String, String> f = (String x) -> x.toUpperCase();
f.apply("msg");
lambda表達式可以賦值給函數式接口,這裏涉及到目標類型上下文,編譯器根據lambda表達式所在的上下文推導其目標類型為Function。
List<String> list = new ArrayList<>();
list.forEach((x) -> System.out.println(x));
lambda表達式作為方法參數,其實是list.forEach方法中的方法參數是函數式接口,編譯器推導lambda表達式類型為forEach中的函數式接口類型作為lambda表達式的目標類型。
從以上可以看出:
lambda表達式的目標類型必須是函數式接口,但是函數式接口和lambda表達式是兩部分,函數式接口並不屬於lambda表達式一部分,只是作為其目標類型;
函數式接口的抽象方法參數必須與lambda表達式的相一致:數量和個數;
函數式接口抽象方法返回參數與lambda表達式返回值一致:類型;
lambda表達式中拋出的受檢異常與函數式接口抽象方法上的throws保持一致;
函數式接口使得lambda表達式能夠更好的使用。Java是面向對象,如果在此基礎上引入lambda,勢必需要向前兼容:
如果引入新的類型,勢必要與舊的api形成兩套體系,這樣就無法在歷史版本中使用lambda表達式;
如果引入新的類型,Java類庫需要對於同一功能就需要維護兩份:過去歷史版本/lambda版本;
如果使用已有的接口表示:
接口是 Java 類型系統的一部分
接口天然就擁有其運行時表示(Runtime representation);
基於這些原因,選擇已有的接口類型作為函數式接口,然後引入lambda表達式是最折中平衡的方式。
Lambda表達式與匿名內部類
lambda表達式的簡潔、緊湊的語法結構是匿名內部類所不能比擬的;
lambda表達式具有更明確的語義——因為只關註有效的代碼;
lambda表達式的作用域比匿名內部類更加有友好(詞法作用域/新的內部作用域):this在lambda表達式中表示外部內,而在內部類中表示當前內部類實例;變量名在lambda表達式中就是表示外部的,而在內部類中要防止繼承自超類———lambda的詞法作用域;(shadow問題)
lambda是函數式編程的體現,內部類任然是在面向對象層面;
參考
深入理解Java 8 Lambda(語言篇——lambda,方法引用,目標類型和默認方法)
The Java? Language Specification
Lambda Expressions
Java 8——Lambda表達式