kotlin作用域函数

官方释义作用域函数:

Kotlin 标准库包含几个函数,它们的唯一目的是在对象的上下文中执行代码块。当对一个对象调用这样的函数并提供一个 lambda 表达式时,它会形成一个临时作用域。在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。共有以下五种:let、run、with、apply 以及 also。

当前使用的kotlin版本为1.3.72;
用官方的区别方式,主要从上下文对象引用方式和返回值加以区分:

函数 引用方式 返回值
run this 函数执行结果
with this 函数执行结果
apply this 调用者对象
also it 调用者对象
let it 函数执行结果

引用方式的区别:

  • 上下文对象作为了lambda函数的接收者,则使用this引用;
  • 上下文对象作为了lambda函数的参数,则使用it引用;

简单来说,如果上下文对象被传入lambda函数中当作参数了,则只能使用it了。、

返回值则视源码真正的返回值而定,对于作用域函数而言,要么返回对象本身,要么返回执行结果。

区别从哪里来,自然可以从源码中得出。

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

引用方式:let使用上下文对象this作为lambda表达式参数,使用it引用;
返回值:返回了函数执行结果
常见场景:1.非空对象执行代码块,2.引入局部变量提高可读性
如:

    fun testLet() {
        var s: String? = "abc"
        s = if (System.currentTimeMillis().rem(2) == 0L) null else s
        s?.let { key ->
            println(key.length)
        }
    }
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
}

引用方式:also使用上下文对象this作为lambda表达式参数,使用it引用;
返回值:返回了调用者(上下文)对象本身
常见场景:不会改变对象的操作;
如:

    fun testAlso() {
        var s: String = "abc"
        s.also {
            println(it.length)
        }
            .plus("d")
            .also {
                println(it.length)
            }
        print(s) //s仍然是abc
    }
run
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

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

引用方式:run作为lambda表达式的接收者,使用this引用;
返回值:返回了函数执行结果
常见场景:1.对象初始化与其他操作并行时,2.作为额外扩展代码块

    fun testRun() {
        val bytes: IntArray = IntArray(3)
        val s = bytes.run {
            bytes[0] = 11
            bytes[1] = 12
            bytes[2] = 13
            bytes[0] + bytes[1] + bytes[2]
        }
        println(s)
    }

run有两种调用方式,前者需要使用kotlin.run来调用,后者方可以如其他函数一样使用T.run进行调用;

        kotlin.run { }
        run{  }

上面两个run函数分别调用前后两种方式,但作用可视为等价——将当前对象作为lambda函数接收者。

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

引用方式:作为lambda表达式的接收者,使用this引用;
返回值:返回了调用者(上下文)对象本身
常见场景:对象赋值;
如:

    fun testApply() {
        val s = IntArray(3).apply {
            this[0] = 11
            this[1] = 12
            this[2] = 13
        }
        println(s.last())
    }
with
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

引用方式:作为lambda表达式的接收者,使用this引用;
返回值:返回了调用者(上下文)对象本身
with函数的区别在于需要自行传入lambda函数的接收者或调用者;
常见场景:对于某个对象执行某种操作;
如:

    fun testWith() {
        var s: String = "abc"
        with(s) {
            for (i in s) {
                println(i)
            }
        }
    }
callsInPlace

上面的源码中均出现了callsInPlace的调用:

@ContractsDsl public fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace

按源码中的注释,这个函数有两个作用,一是确认当前函数调用的位置是否恰当,二是确认当前函数调用的次数;

其实通篇下来的内容,官方文档都配有详细的解释与举例,只需要结合源码加以理解,这5个函数并不难运用。

最后记忆一下:

函数 引用方式
run return block();
let return block(this);
apply block(); return this;
also block(this); return this;
with return receiver.block();

with最特殊,需要参数;
run最简明,直接返回一个函数结果;
let和run相似,但要将自身作为函数参数传入;
apply在函数执行后,返回了自身对象;
also在自身作为参数的函数执行后,返回了自身对象;

以上。

你可能感兴趣的:(Android)