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

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

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