Lambda介紹:作為函數(shù)參數(shù)的代碼塊
用匿名內(nèi)部類實現(xiàn)監(jiān)聽器
<!--Java-->
button.setOnClickListener(new OnClickListener(){
override
public void onClick(View view){
<!-- 點擊后執(zhí)行的動作-->
}
}
);
現(xiàn)在用Kotlin的Lambda表達(dá)式來替換匿名內(nèi)部類
button.setOnClickListener{<!--點擊后執(zhí)行的動作-->}
Lambda和集合
先看一個例子
data class Person(val name:String,val age:Int)
然后創(chuàng)建一個Person集合,并找出集合中年齡最大的那個
val list = listOf(Person("Alice",29),Person("Bob",31))
println(list.maxBy{it.age})
Person{name=Bob,age=31}
如上使用了Kotlin的庫函數(shù),maxBy函數(shù)可以在任何集合上調(diào)用,且只需要一個實參:一個函數(shù),指定比較哪個值來找到最大值,而花括號中的代碼{it.age}就是實現(xiàn)了這個邏輯的lambda
如果lambda剛好是屬性的委托,可以用成員引用代替
list.maxBy(Person::age)
Lambda表達(dá)式語句

還可以把lambda表達(dá)式存儲在一個變量中
val sum = { x:Int,y:Int -> x+y }
println(sum(1,2))
還可以直接調(diào)用lambda表達(dá)式
{println(42)}()
但是這樣的語法毫無可讀性,也沒有什么意義,如果你確實需要把一小段代碼封閉在一個代碼塊中,可以使用庫函數(shù)run來執(zhí)行傳給它的lambda
run { println(42) }
再回到上面的例子
val list = listOf(Person("Alice",29),Person("Bob",31))
println(list.maxBy{it.age})
如果不用簡明的語法重寫這個例子,你會得到下面的代碼
list.maxBy( { p:Person -> p.age} )
這個代碼就一目了然了,花括號里面的代碼片段是lambda表達(dá)式,把它作為實參傳遞給函數(shù)。這個lambda接受一個Person的參數(shù)并返回它的年齡
這個代碼還可以簡化,如果lambda表達(dá)式是函數(shù)調(diào)用的最后一個實參,它可以放到括號的外面
list.maxBy(){ p:Person -> p.age }
當(dāng)lambda是函數(shù)唯一的實參時,還可以去掉調(diào)用代碼中的空括號
list.maxBy { p:Person -> p.age }
省略lambda參數(shù)類型,和局部變量一樣,如果lambda參數(shù)的類型可以被推導(dǎo)出來,你就不需要顯示地指定它。這里以maxBy函數(shù)為例,其參數(shù)的類型始終和集合中元素的類型相同
list.maxBy { p -> p.age }
使用默認(rèn)參數(shù)類型,僅在實參名稱沒有顯示地指定時這個默認(rèn)的名稱才會生成
list.maxBy{ it.age }
注意: it約定能大大縮短你的代碼,但你不應(yīng)該濫用它。尤其在嵌套lambda的情況下,最后顯示地聲明每個lambda的參數(shù)。否則很難搞清楚it引用的到底是哪個值。
此外,lambda表達(dá)式還可以包含更多的語句
val sum = { x:Int,y:Int ->
println("Computing the sum of $x and $y...")
x+y
}
println(sum(1,2)
Computing the sum of 1 and 2...
3
在作用域中訪問變量
當(dāng)在函數(shù)中聲明一個匿名內(nèi)部類的時候,在匿名內(nèi)部類中可以引用函數(shù)的參數(shù)和局部變量。如果函數(shù)內(nèi)部使用lambda,也可以訪問這個函數(shù)的參數(shù)
fun printMessageWithPrefix(messages:Collec tion<String>,prefix:String){
messages.forEach {<!--接收lambda作為實參-->
println("$prefix $it") <!--在lambda中訪問prefix參數(shù)-->
}
}
這里Kotlin和Java的區(qū)別就是,在Kotlin中不會僅限于訪問final變量,在lambda內(nèi)部也可以修改這些變量
成員引用
如果你想要當(dāng)做參數(shù)傳遞的代碼已經(jīng)被定義成了函數(shù),那么你可以將這個函數(shù)轉(zhuǎn)換成值,如下使用::運算符來轉(zhuǎn)換
val getAge = Person::age
這種表達(dá)式被稱為成員引用,它提供簡明語法,來創(chuàng)建一個調(diào)用單個方法或這訪問單個屬性的函數(shù)值。雙冒號把類名稱和你要引用的成員名稱隔開

如下這個lambda表達(dá)式
val getAge = { person:Person -> person.age }
成員引用和調(diào)用函數(shù)的lambda具有一樣的類型,所以可以相互轉(zhuǎn)換
list.maxBy(Person::age)
還可以引用頂層函數(shù),這種情況省略了類名稱,直接以::開頭。成員引用::salute被當(dāng)作實參傳遞給庫函數(shù)run
fun salute() = println("Salute!")
run (::salute)
Salute!
如果lambda要委托給一個接收多個參數(shù)的函數(shù),提供成員引用代替它將會非常方便
val action = {person:Person,message:String -> sendEmail(person,message)}
val nextAction = ::sendEmail
調(diào)用
nextAction(...,...)
可以用構(gòu)造方法引用存儲或者延期執(zhí)行創(chuàng)建類實例的動作,構(gòu)造方法的引用方式是在雙冒號后指定類名稱
val createPerson = ::Person <!--創(chuàng)建`Person`實例的動作被保存成了值-->
val person = createPerson("kdp",25)
println(person)
還可以使用同樣的方式引用擴展函數(shù)
fun Person.isAdult() = age >= 21
val predicate = Person::isAdult
綁定引用
Kotlin1.1允許你使用成員引用語法捕捉特定實例對象上的方法引用
val p = Person("Dmitry",34)
val dmitrysAgeFunction = p::age
println(dmitrysAgeFunction())
注意:dmitrysAgeFunction是一個零函數(shù)的參數(shù),在Kotlin1.1之前,你需要顯示地寫出
lambda{p.age},而不是使用綁定成員引用p::age