有没有可能让函数" Select 性地挂起"?为了用一个例子进行说明,让我们以这个函数为例,该函数包装了一个回调并在前后记录:

fun withLogs(cb: () -> Unit) {
  println("Before");
  cb();
  println("After");
}

现在,我可以使用普通函数调用此函数,但不能使用挂起函数:

fun unsuspend () {
    withLogs({ println("my func") }); // this is fine
}

suspend fun suspended() {
    withLogs(suspend { println("my func") }); // this is not
}

现在我可以为此创建一个显式重载,但它将完全相同,除了几个Suspend关键字:

fun withLogs(cb: () -> Unit) {
  println("Before");
  cb();
  println("After");
}

suspend fun withLogs(cb: suspend () -> Unit) {
  println("Before");
  cb();
  println("After");
}

是否可以编写函数,使其复制CB的挂起状态?例如,这个 idea :

<S: suspend> fun withLogs(S cb: () -> Unit) { /* snip */ }

fun unsuspend () {
    withLogs({ println("my func") }); // suspended
}

suspend fun suspended() {
    withLogs(suspend { println("my func") }); // unsuspended
}


到目前为止,我发现的最好的解决方法是使用runBlocking将未挂起的版本委托给挂起的版本:

fun withLogs(cb: () -> Unit) = runBlocking(withLogs(suspend { cb() }))

但希望有更优雅的东西

推荐答案

如果像这样就地调用函数参数,最简单的方法(以及它在大多数stdlib函数中的实现方式)是将函数设为inline:

inline fun withLogs(cb: () -> Unit) {
  println("Before")
  cb()
  println("After")
}

这之所以有效,是因为这里内联了cb,所以不管它是否被声明为suspend,它的内容将受到调用站点上的约束.

如果您处于挂起上下文中,则可以在lambda中使用挂起函数调用:

suspend fun caller() {
    withLogs {
        delay(100) // suspend is ok because we're in a suspend caller
        println("yeah!")
    }
}

在挂起上下文中为not时,它将正确地给出编译错误:

fun blockingCaller() {
    withLogs {
        delay(100) // compile error
    }
}

如果该函数没有被就地调用(而是被传递),那么您将受到要向其传递回调的其他函数的约束,并且您可能必须在参数中添加一个suspend关键字.这与其说是语言上的限制,不如说是概念上的限制.

Kotlin相关问答推荐

从 Kotlin 的父类获取函数注解

找不到有效的 Docker 环境

具有泛型类型的 Kotlin 密封接口不会为其子类型推断约束

验证构造函数中的值组合

Kotlin 编译器在构造函数中报告未使用的表达式,以便构建器采用 vararg lambda

测试协程和线程之间的差异,我在kotlin中使用线程时无法得到OOM错误

Kotlin 从其他类调用成员扩展函数

使用纯 Kotlin 函数作为 Junit5 方法源

interface扩展

如何退出 Kotlinc 命令行编译器

在 Spring Framework 5.1 中注册具有相同名称的测试 bean

调用单元测试验证伴随对象方法

`DataBindingUtil` 中的 `bind`、`inflate` 和 `setContentView` 有什么区别

如果我可以将 Flow 和 StateFlow 与生命周期范围 \ viewLifecycleOwner.lifecycleScope 一起使用,那么在 ViewModel 中使用 LiveData 有什么意义

类型不匹配:推断类型为 LoginActivity 但应为 LifecycleOwner

任何处理器都无法识别以下选项:'[kapt.kotlin.generated, room.incremental]'

我们如何在Java注释声明中引用Kotlin常量?

类型不匹配推断类型为单位,但应为空

如何在 IntelliJ IDEA 中禁用粘贴时将 Java 转换为 Kotlin?

为什么在 Kotlin 中return可以返回一个return?