Kotlin Lambda 表達(dá)式摘要

基本語法

people.maxBy({ p: Person -> p.age })

如果 lambda 表達(dá)式是函數(shù)調(diào)用的最后一個(gè)實(shí)參,它可以放到括號(hào)的外邊。

people.maxBy() { p: Person -> p.age }

當(dāng) lambda 是函數(shù)唯一的實(shí)參時(shí),你還可以去掉調(diào)用代碼中的空括號(hào)對(duì)。

people.maxBy { p: Person -> p.age }

如果 lambda 參數(shù)的類型可以被推導(dǎo)出來,你就不需要顯式地指定它。

people.maxBy { p -> p.age } // 推導(dǎo)出參數(shù)類型

最后簡(jiǎn)化使用默認(rèn)參數(shù)名稱it代替命名參數(shù)。如果當(dāng)前上下文期望的是只有一個(gè)參數(shù)的 lambda 且這個(gè)參數(shù)的類型可以推斷出來,就會(huì)生成這個(gè)名稱。

people.maxBy { it.age } // “it”是自動(dòng)生成的參數(shù)名稱

如果你用變量存儲(chǔ) lambda,那么就沒有可以推斷出參數(shù)類型的上下文,所以你必須顯式地指定參數(shù)類型:

>>> val getAge = { p: Person -> p.age }
>>> people.maxBy(getAge)

訪問變量

Kotlin 和 Java 的一個(gè)顯著區(qū)別就是,在 Kotlin 中你不會(huì)僅限于訪問 final 變量。在 lambda 內(nèi)部你也可以修改這些變量。

fun printProblemCounts(responses: Collection<String>) {
    var clientErrors = 0 // 聲明將在 lambda 內(nèi)部訪問的變量
    var serverErrors = 0
    responses.forEach {
        if (it.startsWith("4")) {
            clientErrors++ // 在 lambda 里修改變量
        } else if (it.startsWith("5")) {
            serverErrors++
        }
    }
    println("$clientErrors client errors, $serverErrors server errors")
}

成員引用

Kotlin 和 Java 8 一樣,如果你把函數(shù)轉(zhuǎn)換成一個(gè)值,你就可以傳遞它。你使用::運(yùn)算符來轉(zhuǎn)換,這種表達(dá)式稱為成員引用

val getAge = Person::age

你還可以引用頂層函數(shù)(不是類的成員):

fun salute() = println("Salute!")
>>> run(::salute) // 引用頂層函數(shù)
Salute!

注意你還可以用同樣的方式引用擴(kuò)展函數(shù):

fun Person.isAdult() = age >= 21
val predicate = Person::isAdult

你可以用構(gòu)造方法引用存儲(chǔ)或者延期執(zhí)行創(chuàng)建類實(shí)例的動(dòng)作。構(gòu)造方法引用的形式是在雙冒號(hào)后指定類名稱

data class Person(val name: String, val age: Int)

>>> val createPerson = ::Person // 創(chuàng)建“Person”實(shí)例的動(dòng)作被保存成了值
>>> val p = createPerson("Alice", 29)
>>> println(p)
Person(name=Alice, age=29)

使用 Java 函數(shù)式接口

在 Java 中我們隨處可見這樣的代碼

button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        ...
    }
});

在 Kotlin 中,你可以傳遞一個(gè) lambda,代替這個(gè)實(shí)例

button.setOnClickListener { view -> ... }

但是你需要注意,只有在調(diào)用 Java 中的類似函數(shù)時(shí)才能縮寫成這樣簡(jiǎn)單的lambda,如果你調(diào)用的是Kotlin 的函數(shù),你需要在 lambda 前面顯示的加上類型名字,強(qiáng)制將 lambda 轉(zhuǎn)換成對(duì)應(yīng)的接口類型

 setRunnable(Runnable { println("All done!") })

OnClickListener接口只有一個(gè)抽象方法,這種接口被稱為函數(shù)式接口,或者SAM 接口,SAM 代表單抽象方法[譯注:_S_ingle _A_bstract _M_ethod]。Java API 中隨處可見像RunnableCallable這樣的函數(shù)式接口。

當(dāng)然我們也可以通過匿名對(duì)象來實(shí)現(xiàn)

button.setOnClickListener(object : OnClickListener { // 把對(duì)象表達(dá)式作為函數(shù)式接口的實(shí)現(xiàn)傳遞
    override fun onClick(View v) {
        ...
    }
})

不過很顯然,這樣的寫法并不簡(jiǎn)潔。

還有一個(gè)重要的不同是,當(dāng)你顯式地聲明對(duì)象時(shí),每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的實(shí)例。使用 lambda 的情況不同:如果 lambda 沒有訪問任何來自定義它的函數(shù)的變量,相應(yīng)的匿名類實(shí)例可以在多次調(diào)用之間重用:

button.setOnClickListener { view -> ... }// 整個(gè)程序只會(huì)創(chuàng)建一個(gè) OnClickListener 的實(shí)例

然后,如果 lambda 從包圍它的作用域里捕捉了變量,每次調(diào)用就不再可能重用同一個(gè)實(shí)例了。這種情況下,每次調(diào)用時(shí)編譯器都要?jiǎng)?chuàng)建一個(gè)新對(duì)象,其中存儲(chǔ)著被捕捉的變量的值。

fun handleComputation(id: String) { // lambda 會(huì)捕捉”id“這個(gè)變量
    postponeComputation(1000) { println(id) } // 每次 handleComputation 調(diào)用時(shí)都創(chuàng)建一個(gè) Runnable 的新實(shí)例
}

SAM 構(gòu)造方法顯式地把 lambda 轉(zhuǎn)換成函數(shù)式接口

fun createAllDoneRunnable(): Runnable {
    return Runnable { println("All done!") }
}
>>> createAllDoneRunnable().run()
All done!
val listener = OnClickListener { view ->
    val text = when (view.id) { // 使用 view.id 來判斷點(diǎn)擊的是哪個(gè)按鈕
        R.id.button1 -> "First button"
        R.id.button2 -> "Second button"
        else -> "Unknown button"
    }
    toast(text) // 把“text”的值顯示給用戶
}
button1.setOnClickListener(listener)
button2.setOnClickListener(listener)

“with”函數(shù)

來看一個(gè)例子:

fun alphabet(): String {
    val result = StringBuilder()
    for (letter in 'A'..'Z') {
         result.append(letter)
    }
    result.append("\nNow I know the alphabet!")
    return result.toString()
}
>>> println(alphabet())
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Now I know the alphabet!

使用 with函數(shù)改造

fun alphabet(): String {
    val stringBuilder = StringBuilder()
    return with(stringBuilder) { // 指定接收者的值,你會(huì)調(diào)用它的方法
        for (letter in 'A'..'Z') {
            this.append(letter) // 通過顯式的“this”來調(diào)用接收者值的方法
        }
        append("\nNow I know the alphabet!") // 省掉“this”也可以調(diào)用方法,
        this.toString() // 從 lambda 返回值
    }
}

進(jìn)一步改造

fun alphabet() = with(StringBuilder()) {
    for (letter in 'A'..'Z') {
        append(letter)
    }
    append("\nNow I know the alphabet!")
    toString()
}

with返回的值是執(zhí)行 lambda 代碼的結(jié)果。該結(jié)果就是 lambda 里的最后一個(gè)表達(dá)式(的值)。

但有時(shí)候你想返回的是接收者對(duì)象,而不是執(zhí)行 lambda 的結(jié)果。這時(shí)apply庫函數(shù)就派上用場(chǎng)了。

“apply”函數(shù)

apply函數(shù)幾乎和with函數(shù)一模一樣;唯一的區(qū)別是apply始終會(huì)返回作為實(shí)參傳遞給它的對(duì)象(換句話說,接收者對(duì)象)。讓我們?cè)僖淮沃貥?gòu)alphabet函數(shù),這一次用的是apply

fun alphabet() = StringBuilder().apply {
    for (letter in 'A'..'Z') {
        append(letter)
    }
    append("\nNow I know the alphabet!")
}.toString()

另外,在kotlin標(biāo)準(zhǔn)庫中查看apply函數(shù)的源碼

inline fun <T> T.apply(block: T.() -> Unit): T

你會(huì)發(fā)現(xiàn)T.() -> Unit,我們平時(shí)定義函數(shù)類型參數(shù)難道不是() -> Unit嗎?

這里是有區(qū)別的,前者表示帶有接受者的函數(shù)類型,也就是在這個(gè)函數(shù)內(nèi)部的this表示的是接受者實(shí)例。而后者使用的this表示包含這個(gè)函數(shù)的外部類。

最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評(píng)論 9 118
  • 寫在開頭:本人打算開始寫一個(gè)Kotlin系列的教程,一是使自己記憶和理解的更加深刻,二是可以分享給同樣想學(xué)習(xí)Kot...
    胡奚冰閱讀 1,408評(píng)論 0 6
  • 原文鏈接:https://github.com/EasyKotlin 值就是函數(shù),函數(shù)就是值。所有函數(shù)都消費(fèi)函數(shù),...
    JackChen1024閱讀 6,317評(píng)論 1 17
  • 2018-05-16第三十篇原創(chuàng)文章 今天天氣太熱,還刮起了妖風(fēng)。但并不感覺到一絲涼爽,室外就是一個(gè)蒸籠,室內(nèi)開著...
    骨羽閱讀 1,450評(píng)論 0 3
  • hi,你們好。我是小白訓(xùn)練營(yíng)45期24班六組12號(hào)W. 像一切有老師有作業(yè)的學(xué)習(xí)一樣,我迎來了小白的畢業(yè),在大神的...
    叫老吳的閱讀 327評(píng)論 1 0

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