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á)式來(lái)替換匿名內(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的庫(kù)函數(shù)隘膘,maxBy
函數(shù)可以在任何集合上調(diào)用愿汰,且只需要一個(gè)實(shí)參:一個(gè)函數(shù)粹排,指定比較哪個(gè)值來(lái)找到最大值桨啃,而花括號(hào)中的代碼{it.age}
就是實(shí)現(xiàn)了這個(gè)邏輯的lambda
如果lambda剛好是屬性的委托忍燥,可以用成員引用代替
list.maxBy(Person::age)
Lambda表達(dá)式語(yǔ)句
還可以把lambda
表達(dá)式存儲(chǔ)在一個(gè)變量中
val sum = { x:Int,y:Int -> x+y }
println(sum(1,2))
還可以直接調(diào)用lambda
表達(dá)式
{println(42)}()
但是這樣的語(yǔ)法毫無(wú)可讀性墓卦,也沒有什么意義掷贾,如果你確實(shí)需要把一小段代碼封閉在一個(gè)代碼塊中睛榄,可以使用庫(kù)函數(shù)run
來(lái)執(zhí)行傳給它的lambda
run { println(42) }
再回到上面的例子
val list = listOf(Person("Alice",29),Person("Bob",31))
println(list.maxBy{it.age})
如果不用簡(jiǎn)明的語(yǔ)法重寫這個(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)出來(lái),你就不需要顯示地指定它衩椒。這里以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á)式還可以包含更多的語(yǔ)句
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
在作用域中訪問(wèn)變量
當(dāng)在函數(shù)中聲明一個(gè)匿名內(nèi)部類的時(shí)候冀痕,在匿名內(nèi)部類中可以引用函數(shù)的參數(shù)和局部變量荔睹。如果函數(shù)內(nèi)部使用lambda
,也可以訪問(wèn)這個(gè)函數(shù)的參數(shù)
fun printMessageWithPrefix(messages:Collec tion<String>,prefix:String){
messages.forEach {<!--接收l(shuí)ambda作為實(shí)參-->
println("$prefix $it") <!--在lambda中訪問(wèn)prefix參數(shù)-->
}
}
這里Kotlin和Java的區(qū)別就是言蛇,在Kotlin中不會(huì)僅限于訪問(wèn)final
變量僻他,在lambda
內(nèi)部也可以修改這些變量
成員引用
如果你想要當(dāng)做參數(shù)傳遞的代碼已經(jīng)被定義成了函數(shù),那么你可以將這個(gè)函數(shù)轉(zhuǎn)換成值腊尚,如下使用::
運(yùn)算符來(lái)轉(zhuǎn)換
val getAge = Person::age
這種表達(dá)式被稱為成員引用吨拗,它提供簡(jiǎn)明語(yǔ)法,來(lái)創(chuàng)建一個(gè)調(diào)用單個(gè)方法或這訪問(wèn)單個(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í)參傳遞給庫(kù)函數(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允許你使用成員引用語(yǔ)法捕捉特定實(shí)例對(duì)象上的方法引用
val p = Person("Dmitry",34)
val dmitrysAgeFunction = p::age
println(dmitrysAgeFunction())
注意:dmitrysAgeFunction是一個(gè)零函數(shù)的參數(shù)哈恰,在Kotlin1.1之前,你需要顯示地寫出
lambda{p.age}
志群,而不是使用綁定成員引用p::age