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

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

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

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