Kotlinx.serialization 对多态对象(sealed class )支持更好用

为什么需要「多态对象」?

多个数据类型 共享一个父类型 时,我们希望都能用 同一种方式 序列化/反序列化。

现实类比:

比如消息系统里同属于 Message,但内容不同:

类型 数据结构
文本消息 { "type": "text", "content": "hello" }
图片消息 { "type": "image", "url": "xxx.png" }

这些都是 Message,但字段不同。

你想写一个 List<Message>:

Kotlin 复制代码
val list: List<Message> = listOf(
    TextMessage("hello"),
    ImageMessage("https://img.png")
)

但如果没有"多态"能力,序列化并不知道该怎么转成 JSON,更不知道怎么解析回来。

Kotlin 的 sealed class = "受控的多态"

sealed class = 父类 + 多个明确的子类(受限制)

好处:编译器能知道所有子类型 → 更安全也更容易序列化。

图理解👇:

Kotlin 复制代码
      Message  ← Sealed Class
       /    \
 TextMessage  ImageMessage

Kotlinx.serialization 最爽的地方:天然支持 sealed class

只要用 @Serializable 标记 sealed class + 子类即可。

📌 注意:每个子类都要加 @Serializable

Kotlin 复制代码
@Serializable
sealed class Message {

    @Serializable
    @SerialName("text")
    data class TextMessage(val content: String) : Message()

    @Serializable
    @SerialName("image")
    data class ImageMessage(val url: String) : Message()
}

序列化 ------ 用 Json 编码成字符串

Kotlin 复制代码
val json = Json {
    classDiscriminator = "type"  // JSON 的类型字段名
}

val messages: List<Message> = listOf(
    Message.TextMessage("你好"),
    Message.ImageMessage("xx.png")
)

val jsonStr = json.encodeToString(messages)
println(jsonStr)

输出 JSON:

Kotlin 复制代码
[
  {
    "type": "text",
    "content": "你好"
  },
  {
    "type": "image",
    "url": "xx.png"
  }
]

你看到 "type": "text""type": "image" 就是用于表示它是哪种子类。

反序列化 ------ Json 字符串解析回对象

Kotlin 复制代码
val backToObject = json.decodeFromString<List<Message>>(jsonStr)
println(backToObject)

解析结果:

Kotlin 复制代码
[
  TextMessage(content=你好),
  ImageMessage(url=xx.png)
]

就是这么丝滑,完全不用自己判断类型!

对比传统方案(比如 Gson/Moshi)

传统做法:

  • 需要写 Adapter/Factory

  • 手动注册每个子类

  • JSON → 手动判断 "type"

👉 Kotlinx.serialization:一行都不用写,自动处理。

传统方案(示例)

场景设定

有一个父类型:Animal

有两个子类:CatDog

后端返回的 JSON 大概是这样:

Kotlin 复制代码
{"type":"cat","name":"Tom","lives":9}
{"type":"dog","name":"Spike","hasBone":true}
1. 定义父类和子类
Kotlin 复制代码
open class Animal

data class Cat(
    val name: String,
    val lives: Int
) : Animal()

data class Dog(
    val name: String,
    val hasBone: Boolean
) : Animal()
2. 写一个"适配器"(JsonDeserializer)

这个适配器负责:

  • 先读出 "type" 字段

  • 根据值是 "cat" 还是 "dog"

  • 再交给 Gson 去反序列化成具体子类

Kotlin 复制代码
import com.google.gson.*
import java.lang.reflect.Type

class AnimalDeserializer : JsonDeserializer<Animal> {
    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Animal {
        val obj = json.asJsonObject
        val type = obj.get("type")?.asString
            ?: throw JsonParseException("missing type")

        return when (type) {
            "cat" -> context.deserialize<Cat>(obj, Cat::class.java)
            "dog" -> context.deserialize<Dog>(obj, Dog::class.java)
            else  -> throw JsonParseException("unknown type: $type")
        }
    }
}

核心就是 when (type) 那几行:手动判断 "type" → 手动决定用哪个子类去解析。

3. 在 Gson 里注册这个适配器
Kotlin 复制代码
val gson = GsonBuilder()
    .registerTypeAdapter(Animal::class.java, AnimalDeserializer())
    .create()
4. 使用:把 JSON 转成 Animal / List<Animal>

4.1 单个对象

Kotlin 复制代码
val jsonCat = """{"type":"cat","name":"Tom","lives":9}"""
val cat: Animal = gson.fromJson(jsonCat, Animal::class.java)
// 实际类型是 Cat
println(cat)  // 输出:Cat(name=Tom, lives=9)

4.2 List<Animal>(要用 TypeToken)

Kotlin 复制代码
val jsonList = """
[
  {"type":"cat","name":"Tom","lives":9},
  {"type":"dog","name":"Spike","hasBone":true}
]
""".trimIndent()

val listType = object : com.google.gson.reflect.TypeToken<List<Animal>>() {}.type
val animals: List<Animal> = gson.fromJson(jsonList, listType)

println(animals)
// [Cat(name=Tom, lives=9), Dog(name=Spike, hasBone=true)]
对比:

传统 Gson 多态:

  1. JSON 里要有个 "type" 字段(你自己约定)

  2. 写一个 JsonDeserializer<父类> / TypeAdapter<父类>

  3. 在里面:

    • "type"

    • when (type) 分支判断

    • context.deserialize(...) 解析成对应的子类

  4. GsonBuilder.registerTypeAdapter(父类::class.java, 你的Adapter)

现在用 Kotlinx.serialization:

  • sealed class + @Serializable

  • Json { classDiscriminator = "type" }

  • 子类加 @SerialName("cat") / @SerialName("dog")

  • 根本不用自己写 "Adapter/Factory + when 判断" 那一坨。

sealed class 带来的额外好处

  • 编译器会强制你处理所有子类(更安全)

  • IDE 自动提示 when 不漏 case

例如:

Kotlin 复制代码
when (msg) {
    is Message.TextMessage -> println("文本: " + msg.content)
    is Message.ImageMessage -> println("图片: " + msg.url)
    // 如果你忘记某个 case,会编译错误
}

总结(懂 sealed class = 懂多态序列化)

项目 含义
sealed class 定义父类 + 所有子类
多态 不同子类共享同一父类型
Kotlinx.serialization 支持 sealed class 自动识别类型,自动序列化

一句话:sealed class + Kotlinx.serialization = 多态序列化无痛方案

相关推荐
CodeAmaz2 小时前
通用 List 分批切割并循环查询数据库工具类
java·数据结构·工具类·分页
九河云2 小时前
不同级别华为云代理商的增值服务内容与质量差异分析
大数据·服务器·人工智能·科技·华为云
少卿2 小时前
React Compiler 完全指南:自动化性能优化的未来
前端·javascript
广州华水科技2 小时前
水库变形监测推荐:2025年单北斗GNSS变形监测系统TOP5,助力基础设施安全
前端
广州华水科技2 小时前
北斗GNSS变形监测一体机在基础设施安全中的应用与优势
前端
七淮2 小时前
umi4暗黑模式设置
前端
8***B2 小时前
前端路由权限控制,动态路由生成
前端
SongYuLong的博客2 小时前
Ubuntu24.04搭建GitLab服务器
运维·服务器·gitlab
leonardee3 小时前
Spring Security安全框架原理与实战
java·后端