快速上手 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"
}
复制代码

以上就是本节内容,欢迎你们关注~

长按关注
相关文章
相关标签/搜索