Android kotlin 为函数定义函数(高阶函数)
一、背景
使用协程代码时,我们可能会有如下代码来使用 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
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────