【问题标题】:Result nested twice when collecting a Flow in Kotlin 1.5 (kotlin.Result cannot be cast to `Type`)在 Kotlin 1.5 中收集 Flow 时结果嵌套两次(kotlin.Result 不能转换为 `Type`)
【发布时间】:2021-05-17 17:45:33
【问题描述】:

最近我将我的项目更新到 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?JVM / IR: ClassCastException with Result object when it is used by a generic method in a suspend call,但该错误不仅看起来相反,而且应该在最新版本中也得到修复。

【问题讨论】:

  • 这绝对是个bug,当切换到1.4.32时,代码运行得很好。
  • 如果你给 Flow builder 一个明确的类型,比如 flow<Result<Foo>> { ... },这个 bug 还会发生吗?
  • @BrianYencho 是的,在任何地方设置显式类型都没有区别。
  • 显然我没有深入挖掘,并且已经报告了一个类似的问题 (KT-46477),我认为这具有相同的根本原因。谢谢@HenryTwist 调查。
  • 有同样的问题,可能与youtrack.jetbrains.com/issue/KT-46915有关

标签: kotlin kotlin-coroutines


【解决方案1】:

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

【讨论】:

  • 我用 1.5.30-M1 查了一下,确实问题好像没有了。感谢您对此进行调查:)
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2022-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    相关资源
    最近更新 更多