快速上手 Kotlin 開發系列之做用域函數(1)

本節介紹 Kotlin 中的做用域函數。bash

做用域函數是什麼

做用域函數是 Kotlin 內置的能夠對數據作一系列變換的函數。它們與集合的操做符很是的類似,可是集合的操做符只能用於集合的數據變換,而做用域函數能夠應用於全部對象,它能夠對全部對象作一系列的操做。閉包

在 Kotlin 中經常使用的做用域函數有五個:app

run {...}
with(T) {...}
let {...}
apply {...}
also {...}
複製代碼

做用域函數的使用

仍是以代碼示例的形式講述,開始前先作一個數據類 User:less

data class User(var name: String)
fun main() {
    val user = User("Changer0")
    //...
}
複製代碼

let 與 run函數

let 與 run 都會返回閉包的執行結果,區別在於 let 有閉包參數,而 run 沒有閉包參數,其中 run 能夠經過 this 來獲取是誰來調用 run 的,也就是說 this 指代了外層的調用對象。這是他們的惟一區別。ui

val letResult = user.let { user: User -> "let: ${user.name}" }
println(letResult)
val runResult = user.run {"let: ${this.name}" }
println(runResult)
複製代碼

特別的,對於 let 函數,根據 Kotlin 的 Lambda 規則若是隻有一個參數時能夠省略參數不寫,使用 it 來代替:this

val letResult = user.let { "let: ${it.name}" }
複製代碼

also 與 applyspa

also 與 apply 都不返回閉包的執行結果,與上面相似,區別在於 also 有閉包參數,而 apply 沒有閉包參數。code

user.also {
    println("also:${it.name}")
}
user.apply {
    println("apply:${this.name}")
}
複製代碼

那他們閉包的返回值結果是什麼呢?cdn

打開該做用域函數的聲明發現,其實這個做用域函數是對泛型 T 作的擴展函數,對於 also/apply 返回的都是它自己:

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
複製代碼

也就是說,咱們能夠連續的去調用這個做用域函數,適合鏈式操做某個對象:

user.also {
    println("also:${it.name}")
}.apply {
    println("apply:${this.name}")
}.name
複製代碼

takeIf 與 takeUnless

takeIf 與 takeUnless 主要是用於判斷。

咱們看下 takeIf 做用域函數,發現閉包只能返回一個 Boolean 類型的值,而且會根據你傳入的 Lambda 表達式的執行結果來作判斷,若是執行結果爲 true 則返回當前對象,不然會返回 null,這也就是爲何 takeIf 的返回值爲 T? 。

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}
複製代碼

一般在使用時,在閉包後面使用 ?. 繼續執行該對象不爲空時的代碼,後面經過 ?: 執行該對象爲空時的代碼:

user.takeIf { it.name.length > 0}?.also { println("name:${it.name}") } ?: println("name 爲空")
複製代碼

takeUnless 則與 takeIf 徹底相反,若是 Lambda 表達式執行結果爲 false 返回當前對象,不然返回 null:

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}
複製代碼

repeat

repeat 函數在以前已經提到過,實際上是對 for-in 形式的循環作的封裝,再也不作具體的介紹:

public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}
複製代碼

with

查看 with 函數發現與其餘做用域函數不一樣,它不是擴展函數,而是一個頂級的函數,傳入參數 receiver 和 閉包 block,返回 receiver 執行閉包的結果,返回值類型爲泛型 R。

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
複製代碼

with 通常用於對某個對象的總體賦值,這點在 Android 開發中尤爲突出,例如對某個 View 屬性的總體賦值,可使用 with。

with(user) {
    this.name = "Changer1007"
}
複製代碼

以上就是本節內容,歡迎你們關注~

長按關注
相關文章
相關標籤/搜索