【发布时间】:2021-05-17 17:45:33
【问题描述】:
最近我将我的项目更新到 Kotlin 1.5 并注意到在将 Flow 与 Result 结合使用时出现了一些奇怪和意外的行为。考虑以下示例:
// 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有关