You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MutableStateFlow<RealmUUID?>二次发射Null抛出类型转换异常问题

MutableStateFlow<RealmUUID?> 二次发射Null触发ClassCastException问题解析

问题重现

在Kotlin测试场景中,使用MutableStateFlow<RealmUUID?>并第二次发射Null值时,会抛出ClassCastException异常。测试代码如下:

@Test
fun mutableStateFlow_null_test() {
    val flow = MutableStateFlow<RealmUUID?>(null)
    runBlocking {
        this.launch {
            flow.collect {
                println(it)
            }
        }
        for (i in 0 .. 10) {
            flow.tryEmit(null)
            delay(100)
            flow.tryEmit(RealmUUID.random())
            delay(100)
        }
    }
}

抛出的异常信息:

class kotlinx.coroutines.internal.Symbol cannot be cast to class io.realm.kotlin.types.RealmUUID (kotlinx.coroutines.internal.Symbol and io.realm.kotlin.types.RealmUUID are in unnamed module of loader 'app')
java.lang.ClassCastException: class kotlinx.coroutines.internal.Symbol cannot be cast to class io.realm.kotlin.types.RealmUUID (kotlinx.coroutines.internal.Symbol and io.realm.kotlin.types.RealmUUID are in unnamed module of loader 'app')
    at io.realm.kotlin.internal.RealmUUIDImpl.equals(RealmUUIDImpl.kt:61)
    at kotlin.jvm.internal.Intrinsics.areEqual(Intrinsics.java:167)
    at kotlinx.coroutines.flow.StateFlowImpl.updateState(StateFlow.kt:329)
    at kotlinx.coroutines.flow.StateFlowImpl.setValue(StateFlow.kt:318)
    at kotlinx.coroutines.flow.StateFlowImpl.tryEmit(StateFlow.kt:370)
    at com.test.app.ExampleUnitTest$mutableStateFlow_null_test$1.invokeSuspend(ExampleUnitTest.kt:39)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)

该问题仅发生在MutableStateFlow<RealmUUID?>类型中,使用基本类型、普通数据类或包含RealmUUID?的包装类(如TestData)的MutableStateFlow时均正常。

异常原因分析

从栈轨迹可以定位到异常根源:

  1. 当StateFlow从非null的RealmUUID实例切换回null值时,会调用Intrinsics.areEqual(oldValue, newValue)对比新旧状态。
  2. 此时oldValue是RealmUUIDImpl实例,newValue是null,Intrinsics.areEqual会触发RealmUUIDImpl.equals(null)调用。
  3. RealmUUIDImpl的equals方法未做null或类型校验,直接将参数强转为RealmUUID类型,导致ClassCastException。

解决方案

临时规避方案

使用包装类包裹RealmUUID?,比如定义一个简单的数据类:

data class RealmUUIDWrapper(val value: RealmUUID?)

然后将StateFlow的类型改为MutableStateFlow<RealmUUIDWrapper>,发射值时用RealmUUIDWrapper(null)和RealmUUIDWrapper(RealmUUID.random())替代直接发射null或RealmUUID实例。

根本修复方案

修改RealmUUIDImpl的equals方法,增加类型校验和null判断:

override fun equals(other: Any?): Boolean {
    if (this === other) return true
    // 先判断是否为同一类型,避免null或其他类型触发强转异常
    if (other !is RealmUUID) return false
    // 后续执行UUID值的对比逻辑
    return this.uuid == other.uuid
}

验证说明

基本类型、普通数据类的equals方法默认会处理null和类型校验,因此不会出现该异常;包装类的equals由Kotlin自动生成,也会正确处理内部字段为null的场景,所以可以正常工作。

内容的提问来源于stack exchange,提问作者pushpull

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:45:35