Android kotlin 为函数定义函数(高阶函数)

2021-10-28  本文已影响0人  雁过留声_泪落无痕

一、背景

使用协程代码时,我们可能会有如下代码来使用 coroutineScope 用于将指定的协程放在一个作用域内执行。

MyActivity#onCreate()

lifecycleScope.launch {
    coroutineScope {
        // 执行我们的 block 代码,且 this 是 CoroutineScope 对象
        assert(this is CoroutineScope)
    }
}

跟进去查看 coroutineScope 的源码时,猛然间可能不知道我们自己的 block 代码是如何执行的。

public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn { uCont ->
        val coroutine = ScopeCoroutine(uCont.context, uCont)
        coroutine.startUndispatchedOrReturn(coroutine, block)
    }
}
internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? {
    initParentJob()
    return undispatchedResult({ true }) {
        block.startCoroutineUninterceptedOrReturn(receiver, this)
    }
}
@kotlin.SinceKotlin @kotlin.internal.InlineOnly public inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(receiver: R, completion: kotlin.coroutines.Continuation<T>): kotlin.Any? { /* compiled code */ }

到最后也没有发现怎么让 block 内的代码执行的,反而是调用了 block 的一个方法。

block 是一个函数,这里就是调用了函数的函数,下面来讲如何定义并调用函数的函数,同时讲解如何让 block 内的代码执行

二、高阶函数

1. 给类定义函数

我们知道,kotlin 中是可以给类添加函数的,比如如下代码,就给 String 添加了一个 doubleLength() 方法,返回字符串长度的两倍。调用 "abc".doubleLength() 则会返回 6

fun String.doubleLength(): Int {
    return this.length * 2
}

2. 给函数定义函数

考虑我们有如下函数,传入的 block 需要返回一个 Int,同时,调用 block 的时候需要传给它一个 String 对象

fun test(block: String.() -> Int) {

}

正常情况,我们应该会直接调用 block 来得到一个 Int 值,如

fun test(block: String.() -> Int) {
    // 两种调用方式都 OK,需要用一个 String 对象去调用 block 方法
    val result1 = "abc".block()
    val result2 = block.invoke("abc")
    LogUtils.d("result1: $result1, result2: $result2")
}

在其它地方调用 test { this.length } 即可,这里的 this 一定是 String 对象,我们只需要返回一个 Int 即可,这里选择了返回 String 的长度,也可以返回一个固定值,如 99

但是我们看到在 startUndispatchedOrReturn 方法中并没有直接用一个 CoroutineScope 对象来调用 block 方法,而是调用了 block 的一个函数,也就是调用了函数的函数。假设我们这里也调用 block 的一个 hehe 函数,如下

fun test(block: String.() -> Int) {
    block.hehe()
}

那么说明什么问题,说明针对 String.() -> Int 这种类型的函数,一定存在它的一个 hehe 函数,即函数的函数,如

fun (String.() -> Int).hehe() {

}

如果仅仅是这样,那么只调用了函数的函数,函数本身其实并没有执行。那么怎么让函数本身(即原始的 block 体)得到执行呢?需要调用 invoke 方法,如

fun (String.() -> Int).hehe() {
    // 这里的 this 是 String.() -> Int 这个函数,必须用一个 String 对象调用它,两种方式都 OK
    val result1 = "abc".this()
    val result2 = this.invoke("abc")
    LogUtils.d("result1: $result1, result2: $result2")
}

这样,我们原始的 block 体就会得到执行,并且传给它的是 "abc" 这样一个实例,我们可以直接返回其长度即可,如

test {
    LogUtils.d("block executed.")
    length
}

完整代码

fun (String.() -> Int).hehe() {
    // 这里的 this 是 String.() -> Int 这个函数,必须用一个 String 对象调用它,两种方式都 OK
    val result1 = "abc".this()
    val result2 = this.invoke("abc")
    LogUtils.d("result1: $result1, result2: $result2")
}

class TestCoroutineActivity : BaseActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        test {
            LogUtils.d("block executed.")
            length
        }
    }

    fun test(block: String.() -> Int) {
        block.hehe()
    }

}

得到的日志如下

2021-10-28 17:16:32.910 15847-15847/ D/hehe:  
    ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    │ main, TestCoroutineActivity$onCreate$3.invoke(TestCoroutineActivity.kt:35)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ block executed.
    └────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2021-10-28 17:16:32.911 15847-15847/ D/hehe:  
    ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    │ main, TestCoroutineActivity$onCreate$3.invoke(TestCoroutineActivity.kt:35)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ block executed.
    └────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2021-10-28 17:16:32.912 15847-15847/ D/hehe:  
    ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    │ main, TestCoroutineActivityKt.hehe(TestCoroutineActivity.kt:13)
    ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
    │ result1: 3, result2: 3
    └────────────────────────────────────────────────────────────────────────────────────────────────────────────────
上一篇下一篇

猜你喜欢

热点阅读