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时均正常。
异常原因分析
从栈轨迹可以定位到异常根源:
- 当
StateFlow从非null的RealmUUID实例切换回null值时,会调用Intrinsics.areEqual(oldValue, newValue)对比新旧状态。 - 此时
oldValue是RealmUUIDImpl实例,newValue是null,Intrinsics.areEqual会触发RealmUUIDImpl.equals(null)调用。 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
相关产品推荐
相关产品推荐

