为什么不能在 Kotlin 中使用对 suspend 函数的方法引用?

Why is it impossible to use method references to `suspend` functions in Kotlin?

我有一个 Job 实例的列表,我想在启动后的某个时候取消这些实例。这看起来如下:

val jobs = arrayListOf<Job>()
//launch and add jobs...
jobs.forEach { it.cancelAndJoin() } // cancels the jobs and waits for completion

很遗憾,无法在此处使用方法引用。原因:cancelAndJoin 是一个 suspend 函数,因为编译器会抱怨:

jobs.forEach (Job::cancelAndJoin) 

"Error:(30, 24) Kotlin: Unsupported [Callable references to suspend functions]"

为什么这不起作用?

更新: 这已经在 Kotlin 1.3.x 中实现了。获取对挂起函数的可调用引用可为您提供 KSuspendFunctionN (N = 0, 1, ...) 的实例。此类型将其 invoke 运算符定义为暂停函数,因此您可以调用此类可调用引用以与直接调用相同的方式暂停协程。


基本上,支持这一点需要额外的语言设计部分,而不是简单地与协程捆绑在一起。

为什么它不平凡?因为当您获取普通函数的可调用引用时,例如String::reversed,你会得到类似 KFunction1<String, String> 的东西。如果你可以用 suspend 函数做同样的事情,你希望得到什么?

如果是一样的KFunctionN<...>,那么有一个明显的问题就是你可以在需要普通函数的地方传递它并调用它,违反了suspend函数只能被调用的规则在协程内部(编译器转换它们的调用站点)。

所以,它应该更具体一些。 (我目前只是推测,对实际设计尝试一无所知)例如,它可能是 SuspendKFunctionN<...>,其 invoke(...) 是一个暂停功能,或者它可能(不太可能)是一种特殊的表示法,仅用于在需要 suspend (T) -> R 的地方传递函数引用,但无论如何,像这样的功能需要彻底的设计才能面向未来。

Kotlin 标准库中目前缺少这些助手,但您可以自己实现。

例如:

suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit {
    val list = this.map { e ->
        async(...) {
            action(e)
        }
    }
    list.forEach { it.await() }
}

但是,现在传递给 async 的上下文取决于您的服务使用的线程模型(即,您是要执行多线程还是要将所有内容都保留在单个线程中)。