Lambda 表達式入門,看這篇就夠了
- 2020 年 2 月 17 日
- 筆記

說出來怕你們不相信,剛接到物業通知,疫情防控升級了,車輛只能出不能進,每戶家庭每天可指派 1 名成員上街採購生活用品。這不是謠言,截個圖自證清白,出自洛陽市湖北路街道處。

看來事態嚴峻,這樣看似好心,但不一定辦好事,去超時搶購的人會更多。不管了,只能窩在家做持久戰了。趁這段時間,多分享一些原創文章給你們——有眼福了呀,多儲備點知識,疫情結束後肯定能派上大用場。今天分享的主題是《Lambda 表達式》,這也是之前一些讀者留言強烈要求我寫一寫的,不好意思,讓你們久等了,現在來滿足你們,為時不晚吧?

01、初識 Lambda
Lambda 表達式描述了一個代碼塊(或者叫匿名方法),可以將其作為參數傳遞給構造方法或者普通方法以便後續執行。考慮下面這段代碼:
() -> System.out.println("沉默王二")
來從左到右解釋一下,()
為 Lambda 表達式的參數列表(本例中沒有參數),->
標識這串代碼為 Lambda 表達式(也就是說,看到 ->
就知道這是 Lambda),System.out.println("沉默王二")
為要執行的代碼,即將「沉默王二」打印到標準輸出流。
有點 Java 基礎的同學應該不會對 Runnable 接口感到陌生,這是多線程的一個基礎接口,它的定義如下:
@FunctionalInterface public interface Runnable { public abstract void run(); }
Runnable 接口非常簡單,僅有一個抽象方法 run()
;細心的同學會發現一個陌生的註解 @FunctionalInterface
,這個註解是什麼意思呢?
我看了它的源碼,裏面有這樣一段注釋:
Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.
大致的意思就是說,通過 @FunctionalInterface
標記的接口可以通過 Lambda 表達式創建實例。具體怎麼表現呢?
原來我們創建一個線程並啟動它是這樣的:
public class LamadaTest { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("沉默王二"); } }).start(); } }
通過 Lambda 表達式呢?只需要下面這樣:
public class LamadaTest { public static void main(String[] args) { new Thread(() -> System.out.println("沉默王二")).start(); } }
是不是很妙!比起匿名內部類,Lambda 表達式不僅易於理解,更大大簡化了必須編寫的代碼數量。

02、Lambda 語法
每個 Lambda 表達式都遵循以下法則:
( parameter-list ) -> { expression-or-statements }
()
中的 parameter-list
是以逗號分隔的參數。你可以指定參數的類型,也可以不指定(編譯器會根據上下文進行推斷)。Java 11 後,還可以使用 var
關鍵字作為參數類型,有點 JavaScript 的味道。
->
相當於 Lambda 的標識符,就好像見到聖旨就見到了皇上。
{}
中的 expression-or-statements
為 Lambda 的主體,可以是一行語句,也可以多行。
可以通過 Lambda 表達式干很多事情,比如說
1)為變量賦值,示例如下:
Runnable r = () -> { System.out.println("沉默王二"); }; r.run();
2)作為 return 結果,示例如下:
static FileFilter getFilter(String ext) { return (pathname) -> pathname.toString().endsWith(ext); }
3)作為數組元素,示例如下:
final PathMatcher matchers[] = { (path) -> path.toString().endsWith("txt"), (path) -> path.toString().endsWith("java") };
4)作為普通方法或者構造方法的參數,示例如下:
new Thread(() -> System.out.println("沉默王二")).start();
需要注意 Lambda 表達式的作用域範圍。
public static void main(String[] args) { int limit = 10; Runnable r = () -> { int limit = 5; for (int i = 0; i < limit; i++) System.out.println(i); }; }
上面這段代碼在編譯的時候會提示錯誤:變量 limit 已經定義過了。
和匿名內部類一樣,不要在 Lambda 表達式主體內對方法內的局部變量進行修改,否則編譯也不會通過:Lambda 表達式中使用的變量必須是 final 的。

03、Lambda 和 this 關鍵字
Lambda 表達式並不會引入新的作用域,這一點和匿名內部類是不同的。也就是說,Lambda 表達式主體內使用的 this 關鍵字和其所在的類實例相同。
來看下面這個示例。
public class LamadaTest { public static void main(String[] args) { new LamadaTest().work(); } public void work() { System.out.printf("this = %s%n", this); Runnable r = new Runnable() { @Override public void run() { System.out.printf("this = %s%n", this); } }; new Thread(r).start(); new Thread(() -> System.out.printf("this = %s%n", this)).start(); } }
Tips:%s
代表當前位置輸出字符串,%n
代表換行符,也可以使用 n
代替,但 %n
是跨平台的。
work()
方法中的代碼可以分為 3 個部分:
1)單獨的 this 關鍵字
System.out.printf("this = %s%n", this);
其中 this 為 main()
方法中通過 new 關鍵字創建的 LamadaTest 對象——new LamadaTest()
。
2)匿名內部類中的 this 關鍵字
Runnable r = new Runnable() { @Override public void run() { System.out.printf("this = %s%n", this); } };
其中 this 為 work()
方法中通過 new 關鍵字創建的 Runnable 對象——new Runnable(){...}
。
3)Lambda 表達式中的 this 關鍵字
其中 this 關鍵字和 1)中的相同。
我們來看一下程序的輸出結果:
this = com.cmower.java_demo.journal.LamadaTest@3feba861 this = com.cmower.java_demo.journal.LamadaTest$1@64f033cb this = com.cmower.java_demo.journal.LamadaTest@3feba861
符合我們分析的預期。

04、最後
儘管 Lambda 表達式在簡化 Java 編程方面做了很多令人驚訝的努力,但在某些情況下,不當的使用仍然會導致不必要的混亂,大傢伙慎用。
不知不覺,今天已經是元宵節了,足不出戶的日子,是不是都快熬成湯圓了??
好了,我親愛的讀者朋友們,以上就是本文的全部內容了。