美文网首页
Android kotlin 为函数定义函数(高阶函数)

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

作者: 雁过留声_泪落无痕 | 来源:发表于2021-10-28 17:21 被阅读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
        └────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    

    相关文章

      网友评论

          本文标题:Android kotlin 为函数定义函数(高阶函数)

          本文链接:https://www.haomeiwen.com/subject/tupvaltx.html