在 Kotlin 1.5 中收集流时结果嵌套两次(kotlin.Result 无法转换为 `Type`)

Result nested twice when collecting a Flow in Kotlin 1.5 (kotlin.Result cannot be cast to `Type`)

最近我将我的项目更新为 Kotlin 1.5 并注意到将 FlowResult 组合时出现一些奇怪的意外行为。考虑以下示例:

// Kotlin 1.5.0
// Coroutines (kotlinx-coroutines-core) 1.5.0

class Foo

interface FooListener {
    fun onSuccess(foo: Foo)
    fun onFailure(error: Throwable?)
}

suspend fun collectData(listener: FooListener) {
    flow { emit(Result.success(Foo())) }
        .collect { result -> // for reference
            when {
                result.isSuccess -> listener.onSuccess(result.getOrThrow())
                result.isFailure -> listener.onFailure(result.exceptionOrNull())
            }
        }
}

fun main() {
    runBlocking {
        collectData(object : FooListener {
            override fun onSuccess(foo: Foo) {
                println(foo)
            }

            override fun onFailure(error: Throwable?) {
                println(error)
            }
        })
    }
}

以上编译但在运行时失败:

Exception in thread "main" java.lang.ClassCastException: kotlin.Result cannot be cast to Foo

经过一些调试后,我注意到收集的值 (result) 似乎是 Result<Result<Foo>> 类型,而不是预期的 Result<Foo>:

suspend fun collectData(listener: FooListener) {
    flow { emit(Result.success(Foo())) }
        .collect { result ->
            println(result) // Output: Success(Success(Foo@...))
            when {
                result.isSuccess -> listener.onSuccess(result.getOrThrow())
                result.isFailure -> listener.onFailure(result.exceptionOrNull())
            }
        }
}

当我放弃在 collectData 中使用外部提供的侦听器或回调函数的想法时,问题就消失了,例如,使用全局定义的东西:

class Foo

interface FooListener {
    fun onSuccess(foo: Foo)
    fun onFailure(error: Throwable?)
}

val listener = object : FooListener {
    override fun onSuccess(foo: Foo) {
        println(foo)
    }

    override fun onFailure(error: Throwable?) {
        println(error)
    }
}

suspend fun collectData() {
    flow { emit(Result.success(Foo())) }
        .collect { result ->
            println(result) // Output: Success(Foo@...)
            when {
                result.isSuccess -> listener.onSuccess(result.getOrThrow())
                result.isFailure -> listener.onFailure(result.exceptionOrNull())
            }
        }
}

fun main() {
    runBlocking {
        collectData()
    }
}

以上运行没有任何错误。


在我看来,这似乎是语言中的一个错误,但我已经关注这个问题很长时间了,我不是 100% 确定,在将它提交到 Kotlin YouTrack 之前我可以征求第二意见.

我已经看到了 Why does Kotlin crash passing generic Result parameter? and JVM / IR: ClassCastException with Result object when it is used by a generic method in a suspend call 但是这个错误不仅看起来是相反的,而且应该也会在最新版本中修复。

我有同样的问题我将 kotlin 版本从 1.5.10 降级到 1.4.32 并且代码 运行 正确。

这在 https://youtrack.jetbrains.com/issue/KT-46915 中进行了跟踪,看起来修复将与 Kotlin 1.5.30

一起发布