大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

Kotlin之作用域函数

kotlin

作用域函数

含义:Kotlin中对一个对象进行一系列操作的函数,写法上类似集合的操作符

种类:runletalsoapplytakeIftakeUnlesswithrepeat


第一类:有返回结果的

举例:通过letrun函数输入一段话,letrun函数都是将参数中闭包的返回值当做自己的返回值给返回出来,具体看源码实现。

    // 都是有返回结果,但是let闭包有参数,可以用it指代对象
    // run没有闭包参数的,只能用this来指代对象
    val letResult = user.let { "let::{${it.javaClass}}" }
    println(letResult)
    // let::{class lambda.User}

    val runResult = user.run { "run::{${this.javaClass}}" }
    println(runResult)
    // run::{class lambda.User}

letrun的源码:

// let
kotlin
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

// run
tlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()

通过源码可以看出,let参数block是有返回值的。run同理。但是let中是将T继续以参数的形式传给了block闭包,所以在let闭包中可以使用it来代替对象,而run却只能用this关键字。


第二类:无返回结果

举例:同样是通过alsoapply输出一句话,但是没有返回的是对象,不是闭包函数。具体看下面源码实现。

    // also、apply和let、run的区别在于闭包内的操作没有返回结果
    // also和let一致,有闭包参数,可以使用it
    // apply和run一致,没有闭包参数,只能使用this
    user.also {
        println("also::{${it.javaClass}}")
    }
    // also::{class lambda.User}
    user.apply {
        println("apply::{${this.javaClass}}")
    }
    // apply::{class lambda.User}

源码:

// also
kotlin
@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
}

// apply
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

also的源码一眼就能看到,block参数返回了一个Unit,函数是将调用者T给返回了。apply同理。itthis解释看上面letrun的解释。


第三类:函数的闭包有返回值,但是为Boolean类型

举例:通过takeIftakeUnless函数中的闭包结果进行判断,然后做出相应的操作。其中闭包返回值都为Boolean类型。

// takeIf和takeUnless的闭包参数都会返回一个Boolean类型的值
// takeIf内闭包为false时,taleIf执行完会返回一个null
// takeUnless内闭包为true时,takeUnless执行完会返回一个null,和takeIf正好相反
user.takeIf { it.name.isNotEmpty() }
            ?.also { println("name is {${it.name}}") }
            ?: println("name is null")
user.takeUnless { it.name.isNotEmpty() }
            ?.also { println("name is null") }
            ?: println("name is {${user.name}}")

源码:

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

通过上面takeIf的源码if (predicate(this)) this else null可以看出,如果闭包的返回值为false,那么函数就会返回一个null

takeUnless正好与之相反。


第四类:循环执行函数

    repeat(2) {
        println("count is $it,name is ${user.name}")
    }
    // count is 0,name is taonce
    // count is 1,name is taonce

repeat函数不仅仅用于集合中,它的适用范围是任意对象,需要传入两个参数:times: Int, action: (Int) -> Unittimes是循环的次数,action就是闭包。通过上面的例子就能知道,闭包的执行次数有times确定。


第五类:顶层函数

    with(user) {
        this.name = "kotlin"
        println("user name is ${user.name}")
    }
    // user name is kotlin

with比较特殊,它不是通过对象调用使用的,而是把对象作为参数传入的。适用场景,对同一个对象进行相同的操作。比如一个TextView和一个Button都需要显示文字内容为开始,就可以用with同步操作:

with(view) {
    this.text = "开始"
}

只需要这么写就可以完全同意处理了。

with也是有返回值的,它和letrun很像,最大的不同在于with的参数,可以对同一类型的对象进行相同的操作,比如上面对TextViewButton的操作,如果换做是letrun就不行了,因为它们是通过对象调用的方法。


写在最后

每个人不是天生就强大,你若不努力,如何证明自己,加油!

Thank You!

--Taonce

如果你觉得这篇文章对你有所帮助,那么就动动小手指,扫描下方的二维码,关注一波吧~~非常期待大家的加入

专注Kotlin知识的公众号