Kotlin| 作用域函數

Kotlin有5個作用域函數,分別是let{},run{},with{},apply{}also{},它們都可以接收并且執(zhí)行一個lamda表達式,去掉lamda表達式的argument列表和{},看上去就像在執(zhí)行代碼塊 (KClosure)。拋開Kotlin實現它們的動機和原理,我的目標就是學會怎么用它們!

不知道如何選擇?
首先看Kotlin這一部分的guide,發(fā)現首先要搞懂下面兩個簡單的概念:
1??上下文對象 (Context Object): this versus it
2??返回結果 (Result): lamda versus object

初學編程應該都知道C風格語言喜歡用一對{}表示一個作用域 (Scope),也應該清楚lamda表達式返回結果是最后一行代碼/表達式的執(zhí)行結果。

上下文對象就是調用作用域函數的對象,以let為例就是someObject.let{}里的someObject,而作用域函數內部的lamda表達式要持有someObject的一份引用,那么有兩種方式,一是lamda接收對象的this關鍵字,二是lamda表達式的argument。
原形:

someObject.let {it->}

變體:

someObject.let{}

返回結果要么就是lamda表達式的結果,要么是對象本身。如果是lamda結果,就代表這中間做了一些額外的計算或任務,然后可以賦值給一個局部變量;如果指向對象本身,那就是對象內部成員的配置或操作,然后可以繼續(xù)調用對象方法或return語句/直接返回。

// lamda結果賦值給firstNum變量
val numbers = mutableListOf("one", "two", "three", "four", "five")
val firstNum = numbers.first().let{firstItem -> if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"}

// adam是一個Person對象
val adam = Person("Adam").apply {
    age = 32
    city = "London"        
}

另外,還需要了解一件事,大多數作用域函數都是inline拓展函數(let不是inlne),極個別情況下也可以是inline單獨函數,這2個極個別的例外就是with和沒有接收對象 (前面沒有someObject.)的run。

fun <T, R> T.let(f: (T) -> R): R = f(this) // let原型

// also原型
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

// run獨立函數
val decimalRegex = run {
    val digits = "0-9"
    val sign = "+-"

    Regex("?[$digits$hexDigits]+")
}

for (match in decimalRegex.findAll("+1234")) {
    println(match.value)
}

其他原型看這位簡誘的整理。

最后下表根據這兩個維度對它們進行了分類。

上下文 / 結果 lamda結果 (賦值) 自己 (返回)
it (可重命名) let(拓展) also(拓展)
this (可省略) run(拓展/單獨函數), with(單獨函數) apply(拓展)

最后的最后,可恥地搬運Kotlin文檔的建議

  • 在非空對象執(zhí)行l(wèi)amda:let
  • 在{}寫lamda: someList.filter().map{ it.length }.filter { it > 3 }.let{::println}
  • 設置對象屬性:Person("張三").apply { type="法外狂徒" }
  • 設置對象屬性+調用成員方法:val sum = Pair(1,2).run { operator = "+" compute(x,y,operator) }
  • 定義局部表達式:val expression = run { Regex(".+") }
  • 日志打印或調試之類不修改對象的操作:
val numbers = mutableListOf("one", "two", "three")
numbers.also { println("新增前: $it") }.add("four")
  • 在對像上調用一系列業(yè)務函數:
val zhangsan = Person()
with(zhangsan) {
  doQiangjian(zhangsan) // 張三強X婦女
  doFeifajizi(zhangsan) // 張三非法集資
}

補充:takeIf()takeUnless()接受一個Predicate然后返回null或對象,然后可以組合作用于函數繼續(xù)鏈式調用,其中takeUnless表示除......之外。

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

相關閱讀更多精彩內容

  • 什么是作用域函數(Scope Functions)? Kotlin 標準庫包含了幾個特殊的函數,其目的是在調用對象...
    SkyRiN閱讀 1,077評論 1 4
  • 前言 最近使用kotlin語言開發(fā)了新的項目,kotlin的一些特性和大量的語法糖相當好用,相比于java,開發(fā)效...
    SirWwh閱讀 2,413評論 1 2
  • kotlin中經常出現run、with、let、also、apply,開始時候容易迷糊,有什么用有什么區(qū)別? 一:...
    追夢者wang閱讀 690評論 0 1
  • 上面是常用的五個作用域函數 run let with apply also從定義上我們看出apply 和 also...
    莫庫施勒閱讀 601評論 0 0
  • 先來看一段代碼 "result."頻繁出現 如何去掉繁瑣的出現的變量代碼我們可以在賦值時增加代碼塊 {return...
    gaom明閱讀 463評論 0 1

友情鏈接更多精彩內容