如何在Kotlin中为事件类强制指定EventContent的结构化类型
解决方案
方案一:内部封装强类型转换(兼容现有Event接口)
这种方式不需要修改原有的Event接口,只在ShutdownEvent内部封装ShutdownData与JSON字符串的自动转换逻辑,既满足接口对eventContent为String的要求,又能通过强类型属性操作业务数据。
- 定义强类型的
ShutdownData:
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json // 全局单例Json配置,建议统一管理 private val json = Json { ignoreUnknownKeys = true // 兼容可能的字段扩展 } @Serializable data class ShutdownData( var trigger: String, var location: String, )
- 实现
ShutdownEvent:
data class ShutdownEvent( private var _eventContent: String ) : Event { // 对外暴露强类型属性,自动处理序列化/反序列化 var shutdownData: ShutdownData get() = json.decodeFromString(_eventContent) set(value) { _eventContent = json.encodeToString(value) } // 实现Event接口的String类型属性 override var eventContent: String get() = _eventContent set(value) { _eventContent = value // 可选:验证输入的JSON是否符合ShutdownData格式,避免脏数据 runCatching { json.decodeFromString<ShutdownData>(value) } .onFailure { throw IllegalArgumentException("无效的ShutdownData格式") } } // 便捷构造函数:直接传入ShutdownData,自动序列化为JSON字符串 constructor(data: ShutdownData) : this(json.encodeToString(data)) }
- 事件处理逻辑完全兼容:
fun process(event: Event) { if (event is ShutdownEvent) { // 直接使用强类型的shutdownData属性 val trigger = event.shutdownData.trigger val location = event.shutdownData.location // 执行业务逻辑 } }
方案二:泛型化Event接口(更优雅但需修改原有接口)
如果允许调整Event接口的定义,可以用泛型实现通用的强类型转换逻辑,适合所有事件类型复用:
- 修改
Event接口并实现通用基类:
import kotlinx.serialization.KSerializer interface Event<T> { var eventContent: String // 满足数据库存储的String要求 var typedContent: T // 强类型业务数据 } // 抽象基类封装通用的序列化/反序列化逻辑 abstract class BaseEvent<T>(private val serializer: KSerializer<T>) : Event<T> { override var eventContent: String = "" set(value) { field = value typedContent = json.decodeFromString(serializer, value) } override var typedContent: T get() = json.decodeFromString(serializer, eventContent) set(value) { eventContent = json.encodeToString(serializer, value) } // 便捷构造函数 constructor(data: T) : this() { typedContent = data } constructor() : this(serializer) }
- 实现
ShutdownEvent:
data class ShutdownEvent( override var typedContent: ShutdownData ) : BaseEvent<ShutdownData>(ShutdownData.serializer()) { constructor() : this(ShutdownData("", "")) }
- 事件处理逻辑同样兼容:
fun process(event: Event<*>) { if (event is ShutdownEvent) { val trigger = event.typedContent.trigger val location = event.typedContent.location // 执行业务逻辑 } }
方案选择建议
- 若现有代码大量依赖原
Event接口(无泛型),优先选方案一,改动最小且完全兼容。 - 若项目处于初期或可以统一调整事件结构,选方案二,代码更简洁,可复用性更强。
内容的提问来源于stack exchange,提问作者john
相关产品推荐
相关产品推荐

