開局一張圖,內容全靠摘錄...
一:概述
- 什么是lambda表達式?
- lambda的語法規則
- 為什么需要引入lambda表達式?
什么是lambda表達式?
名詞解釋:lambda,其實是數學符號中的 λ,一個希臘字母。拉姆達 Lambda(大寫Λ,小寫λ),是第十一個希臘字母;在計算機術語中,Lambda 多表達式”是一個匿名函數,可以包含表達式和語句,並且可用於創建委托或表達式目錄樹類型。
Java 8 引入的 Lambda 表達式的主要作用就是簡化部分的寫法;
怎么一個簡化呢?看個例子:
以Lambda語法創建線程和匿名內部類創建線程的區別(顯然代碼少了很多!):
public static void main(String[] args) { // 用匿名內部類的方式來創建線程 new Thread(new Runnable() { @Override public void run() { System.out.println("我是一個線程啊"); } }); // 使用Lambda來創建線程 new Thread(() -> System.out.println("我是一個線程啊")); }
lambda的語法規則
Lambda的語法是這樣的:
圖示;Runnable接口來舉例:
Lambda 表達式在 Java 語言中引入了一個新的語法元素和操作符。這個操作符為 “->”,該操作符被稱 為 Lambda 操作符或箭頭操作符。它將 Lambda 分為兩個部分:
-
左側:指定了 Lambda 表達式需要的所有參數
-
右側:指定了 Lambda 體,即 Lambda 表達式要執行的功能。
1.語法格式一:無參,無返回值,Lambda 體只需一條語句。
示例:
Runnable r1 = () -> System.out.println("Hello Lambda!");
2.語法格式二:Lambda 需要一個參數。
示例:
Consumer<String> con = (x) -> System.out.println(x);
3.語法格式三:Lambda 只需要一個參數時,參數的小括號可以省略。
示例:
Consumer<String> con = x -> System.out.println(x);
4.語法格式四:Lambda 需要兩個參數,並且有返回值。
示例:
Comparator<Integer> com = (x, y) -> {
System.out.println("函數式接口"); return Integer.compare(x, y); };
5.語法格式五:當 Lambda 體只有一條語句時,return 與大括號可以省略。
示例:
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
6.Lambda 表達式的參數列表的數據類型可以省略不寫,因為JVM編譯器通過上下文推斷出,數據類型,即“類型推斷”。
示例:
Comparator<Integer> com = (Integer x,Integer y) -> { //Integer 類型可以省略
System.out.println("函數式接口");
return Integer.compare(x, y); }; BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4 BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5 類型推斷
類型推斷:Lambda 表達式中的參數類型都是由編譯器推斷 得出的。Lambda 表達式中無需指定類型,程序依然可 以編譯,這是因為 javac 根據程序的上下文,在后台 推斷出了參數的類型。Lambda 表達式的類型依賴於上 下文環境,是由編譯器推斷出來的。這就是所謂的 “類型推斷”
使用Labmda表達式需要函數式編程接口,比如在Runnable接口上我們可以看到@FunctionalInterface
注解(標記着這個接口只有一個抽象方法)
在引用一個例子來加深什么是lambda表達式
我們知道,對於一個Java變量,我們可以賦給其一個“值”。
如果你想把“一塊代碼”賦給一個Java變量,應該怎么做呢?
比如,我想把右邊那塊代碼,賦給一個叫做aBlockOfCode的Java變量:
在Java 8之前,這個是做不到的。但是Java 8問世之后,利用Lambda特性,就可以做到了。
當然,這個並不是一個很簡潔的寫法。所以,為了使這個賦值操作更加elegant, 我們可以移除一些沒用的聲明。
這樣,我們就成功的非常優雅的把“一塊代碼”賦給了一個變量。而“這塊代碼”,或者說“這個被賦給一個變量的函數”,就是一個Lambda表達式。
但是這里仍然有一個問題,就是變量aBlockOfCode的類型應該是什么?
在Java 8里面,所有的Lambda的類型都是一個接口,而Lambda表達式本身,也就是”那段代碼“,需要是這個接口的實現。這是我認為理解Lambda的一個關鍵所在,簡而言之就是,Lambda表達式本身就是一個接口的實現。直接這樣說可能還是有點讓人困擾,我們繼續看看例子。我們給上面的aBlockOfCode加上一個類型:


這樣,我們就得到了一個完整的Lambda表達式聲明:

為什么需要引入lambda表達式?
最直觀的作用就是使得代碼變得異常簡潔。
我們可以對比一下Lambda表達式和傳統的Java對同一個接口的實現:
這兩種寫法本質上是等價的。但是顯然,Java 8中的寫法更加優雅簡潔。並且,由於Lambda可以直接賦值給一個變量,我們就可以直接把Lambda作為參數傳給函數, 而傳統的Java必須有明確的接口實現的定義,初始化才行:
有些情況下,這個接口實現只需要用到一次。傳統的Java 7必須要求你定義一個“污染環境”的接口實現MyInterfaceImpl,而相較之下Java 8的Lambda, 就顯得干凈很多。

Lambda結合FunctionalInterface Lib, forEach, stream(),method reference等新特性可以使代碼變的更加簡潔!
直接上例子。
假設Person的定義和List<Person>的值都給定。
現在需要你打印出guiltyPersons List里面所有LastName以"Z"開頭的人的FirstName。
原生態Lambda寫法:定義兩個函數式接口,定義一個靜態函數,調用靜態函數並給參數賦值Lambda表達式。
這個代碼實際上已經比較簡潔了,但是我們還可以更簡潔么?
當然可以。在Java 8中有一個函數式接口的包,里面定義了大量可能用到的函數式接口(java.util.function (Java Platform SE 8 ))。所以,我們在這里壓根都不需要定義NameChecker和Executor這兩個函數式接口,直接用Java 8函數式接口包里的Predicate<T>和Consumer<T>就可以了——因為他們這一對的接口定義和NameChecker/Executor其實是一樣的。

第一步簡化 - 利用函數式接口包:
靜態函數里面的for each循環其實是非常礙眼的。這里可以利用Iterable自帶的forEach()來替代。forEach()本身可以接受一個Consumer<T> 參數。
第二步簡化 - 用Iterable.forEach()取代foreach loop:

第三步簡化 - 利用stream()替代靜態函數:


第四步簡化 - 如果是println(p),則可以利用Method reference代替forEach中的Lambda表達式:

這基本上就是能寫的最簡潔的版本了。

Lambda配合Optional<T>可以使Java對於null的處理變的異常優雅
這里假設我們有一個person object,以及一個person object的Optional wrapper:

Optional<T>如果不結合Lambda使用的話,並不能使原來繁瑣的null check變的簡單。

只有當Optional<T>結合Lambda一起使用的時候,才能發揮出其真正的威力!
我們現在就來對比一下下面四種常見的null處理中,Java 8的Lambda+Optional<T>和傳統Java兩者之間對於null的處理差異。
情況一 - 存在則開干

情況二 - 存在則返回,無則返回屁

情況三 - 存在則返回,無則由函數產生

情況四 - 奪命連環null檢查

由上述四種情況可以清楚地看到,Optional<T>+Lambda可以讓我們少寫很多ifElse塊。尤其是對於情況四那種奪命連環null檢查,傳統java的寫法顯得冗長難懂,而新的Optional<T>+Lambda則清新脫俗,清楚簡潔。
資料: