Java Lambdas 筆記
Java 8 引入 Lambda 之後,很多集合操作、條件過濾、回呼寫法都變得簡潔不少。不過剛開始看 Lambda 時,如果沒有把 Functional Interface 這個前提一起理解,常常會有一種「為什麼它可以直接塞進去」的困惑。
這篇把 Lambda 最容易卡住的幾個點整理一下,用比較工程實務的角度來看會比較好理解。
Lambda 為什麼能成立
Lambda 不是憑空冒出來的新語法糖,它背後有一個重要前提:目標型別必須是 Functional Interface。
所謂 Functional Interface,就是只定義一個抽象方法的介面。例如:
|
|
因為這個介面只有一個抽象方法,所以編譯器看到 Lambda 時,才能知道它到底要對應到哪個方法。
常見範例
像下面這種寫法:
|
|
其實就是在建立 Predicate 的實作,等價概念可以想成是把 test() 這個方法補上去,只是寫法更短。
為什麼 removeIf() 可以直接吃 Lambda
以 ArrayList.removeIf() 來看:
|
|
這裡不是 Lambda 自己知道要呼叫什麼,而是 removeIf() 的方法簽名已經規定它要收 Predicate。因此編譯器會把 n -> n < 0 視為 Predicate.test() 的實作。
簡單講,決定 Lambda 角色的是呼叫端的型別,不是 Lambda 本身。
Lambda 好用在哪裡
在下面幾種場景特別常見:
- 集合過濾,例如
filter()、removeIf()。 - 排序比較,例如
Comparator。 - 簡單回呼與策略切換。
- Stream API 串接。
如果這些地方還是都寫匿名類別,程式碼通常會長很多。
例外處理要注意
Lambda 內如果拋出受檢查例外,常常會遇到編譯錯誤。原因不是 Lambda 特別難搞,而是你對應的 Functional Interface 方法簽名沒有宣告該例外。
實務上常見做法有兩種:
- 在 Lambda 裡面 catch 之後轉成 RuntimeException。
- 自訂一個可以宣告 throws 的 Functional Interface。
如果你常常在 Lambda 裡處理 IO、轉檔或網路操作,這一點會很有感。
小結
Java Lambda 最重要的理解點不是語法本身,而是它依附在 Functional Interface 之上。只要先搞懂「目標型別只有一個抽象方法」,再回頭看 Predicate、Function、Consumer 這些介面,整體就會清楚很多。之後在讀 Stream API 或集合操作時,也比較不會只覺得它是魔法。