本文是對<<Kotlin in Action>>
的學習筆記,若是須要運行相應的代碼能夠訪問在線環境 try.kotlinlang.org,這部分的思惟導圖爲: java
Lambda
表達式,本質上是能夠 傳遞給函數的一小段代碼,能夠輕鬆地把通用的代碼結構抽取成庫函數,Kotlin
標準庫就大量地使用了它們。數組
Lambda
的應用場景有:bash
在Java
中,能夠用匿名內部類來實現,可是它的語法很囉嗦,下面咱們演示用Lambda
來實現點擊監聽:數據結構
button.setOnClickListener { /* 點擊後執行的動做 */}
複製代碼
咱們對集合執行的大部分任務都遵循幾個通用的模式,因此實現這幾個模式的代碼應該放在一個庫裏,下面咱們演示一個例子:將Person
數據類放到一個集合當中,並從中選出年齡最大的一我的。 函數
maxBy
函數,它只須要一個實參:
一個函數,指定比較哪一個值來找到最大的元素,花括號中的代碼
{ it.age }
就是實現了這個邏輯的
lambda
,它接收一個集合中的元素做爲實參(使用
it
引用它)而且返回用來比較的值,在上面的例子中:
Person
對象age
屬性中的值若是lambda
恰好是 函數或者屬性的委託,能夠用 成員引用 替換。學習
people.maxBy(Person :: age)
複製代碼
一個Lambda
表達式把一小段行爲進行編碼,你能把它 當作值處處傳遞,它能夠被 獨立地聲明並存儲到一個變量中,可是最多見的仍是直接聲明它並傳遞給函數,下面是一個Lambda
表達式的語法,->
前爲 參數,後爲 函數體,始終用 花括號包圍。編碼
{x : Int, y : Int -> x + y}
複製代碼
能夠將Lambda
表達式存儲在一個變量中,把這個變量當作普通函數對待(即經過相應的實參調用它): spa
若是須要把一小段代碼封閉在一個代碼塊中,可使用庫函數run
,這種調用和內建語言結構同樣高效且不會帶來額外運行時開銷: 3d
如今,讓咱們回到最開始尋找集合中年齡最大的人的例子,它本來的調用方法以下,maxBy
函數接收一個lambda
表達式{ p : Person -> p.age }
做爲參數:code
people.maxBy({ p : Person -> p.age })
複製代碼
上面這段代碼的解釋爲:花括號中的代碼片斷是lambda
表達式,把它做爲實參傳給函數,這個lambda
接收一個類型爲Person
的參數並返回它的年齡。下面,咱們一塊兒來看一下如何簡化這個表達式:
Kotlin
有一個語法規定,若是lambda
表達式是函數調用的 最後一個實參,它能夠 放到括號的外邊,所以上面的例子簡化爲://第一步:將 lambda 表達式放到括號的外邊。
people.maxBy() { p : Person -> p.age }
複製代碼
lambda
是函數 惟一的實參,還能夠 去掉調用代碼中的空括號對//第二步:去掉空括號對。
people.maxBy { p : Person -> p.age }
複製代碼
lambda
參數的類型能夠被推倒出來,你就不須要顯示地指定它,以maxBy
函數爲例,其 參數類型始終和集合的元素類型相同,所以編譯器知道你是對Person
對象的集合調用maxBy
函數,能夠簡化爲://第三步:省略 lambda 參數類型。
people.maxBy { p -> p.age }
複製代碼
可是若是咱們用變量存儲lambda
,那麼就沒有能夠推斷出參數類型的上下文,因此你必須顯示地指定參數類型:
//沒法推斷出參數的類型,必須顯示地指定參數的類型。
val getAge = { p : Person : p.age }
people.maxBy (getAge)
複製代碼
it
代替命名參數://第四步:使用默認參數名稱。
people.maxBy { it.age }
複製代碼
在 Kotlin 知識梳理(2) - 函數的定義與調用 中,咱們經過joinToString
介紹了命名參數和默認參數值的用法,實際上在標準庫中也有定義這個函數,不一樣之處在於它能夠接收一個附加的函數參數,這個函數能夠用toString
函數之外的方法來把一個 元素轉換成字符串,下面顯示如何 只打印出人的名字
lambda
表達式能夠包含更多的語句,最後一個表達式就是lambda
的結果:
當在函數內聲明一個匿名內部類時,可以在這個匿名內部類引用這個函數的參數和局部變量,也能夠用lambda
作一樣的事情,若是在函數內部使用lambda
,也能夠訪問這個函數的參數,還有在lambda
以前定義的局部變量。
下面咱們用標準庫函數forEach
來展現這種行爲,它是最基本的集合操做函數之一:它所作的所有事情就是在集合中的每個元素之上都調用給定的lambda
:
Kotlin
中不會僅限於訪問final
變量,在lambda
內部也能夠修改這些變量,下面的代碼中對給定的相應狀態碼set
分別進行計數。
Kotlin
中,它容許在
lambda
內部訪問非
final
變量甚至修改它們。從
lambda
內訪問外部變量,咱們稱這些
變量被 lambda 捕捉,就像上面例子中的
clientErrors
和
serverErrors
。
默認狀況下,局部變量的生命週期被限制在聲明這個局部變量的函數當中,可是若是它被lambda
捕捉了,使用這個變量的代碼能夠被存儲並稍後執行,原理爲:
final
變量時,它的值和使用這個值的lambda
代碼一塊兒存儲。final
變量,它的值被封裝在一個包裝器中,這樣你就能夠改變這個值,而對這個包裝器的引用會和lambda
代碼一塊兒存儲。Java
只容許捕捉final
變量,而當你想捕捉可變變量的時候,可使用兩種技巧:
這樣,當捕捉了一個可變變量var
的時候,它的值被做爲Ref
類的一個實例被存儲下來,Ref
變量是final
的能輕易被捕捉,而後實際值存儲在其字段中,而且能夠在lambda
內被修改。
在上面的例子中,咱們演示了 如何讓你把代碼塊做爲參數傳遞給函數,可是若是要當作參數傳遞的代碼已經被定義成了函數,這時候就須要 把函數轉換成一個值,這種方式稱爲 成員引用。
val getAge = Person :: age
複製代碼
它提供了簡明的語法,來建立一個 調用單個方法或者訪問單個屬性的函數值,雙冒號把 類名稱 與 你要引用的成員(一個方法或者屬性)名稱 隔開。
成員引用和調用該函數的lambda
具備同樣的類型,因此能夠互換使用:
people.maxBy(Person : age)
複製代碼
除此以外,還能夠引用頂層函數,這裏咱們省略了類名稱,直接以:
開頭,成員引用::salute
被看成實參傳遞給庫函數run
,它會調用相應的函數:
咱們還可使用 構造方法引用 存儲或者延期執行建立類實例的動做,構造方法引用的形式是 在雙冒號後指定類的名稱:
isAdult
方法,選出年齡大於21
的人。
運行結果爲: