Kotlin之Lambda表達(dá)式和成員引用

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容