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

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

现实类比:

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

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

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

你想写一个 List<Message>:

val list: List = listOf(
    TextMessage("hello"),
    ImageMessage("https://img.png")
)

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

Kotlin 的 sealed class = “受控的多态”

sealed class = 父类 + 多个明确的子类(受限制)
好处:编译器能知道所有子类型 → 更安全也更容易序列化。

图理解:

      Message  ← Sealed Class
       /    \
 TextMessage  ImageMessage

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

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

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

@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 编码成字符串

val json = Json {
    classDiscriminator = "type"  // JSON 的类型字段名
}
val messages: List = listOf(
    Message.TextMessage("你好"),
    Message.ImageMessage("xx.png")
)
val jsonStr = json.encodeToString(messages)
println(jsonStr)

输出 JSON:

[
  {
    "type": "text",
    "content": "你好"
  },
  {
    "type": "image",
    "url": "xx.png"
  }
]

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

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

val backToObject = json.decodeFromString>(jsonStr)
println(backToObject)

解析结果:

[
  TextMessage(content=你好),
  ImageMessage(url=xx.png)
]

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

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

传统做法:

  • 需要写 Adapter/Factory

  • 手动注册每个子类

  • JSON → 手动判断 "type"

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

传统方案(示例)

场景设定

有一个父类型:Animal
有两个子类:CatDog

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

{"type":"cat","name":"Tom","lives":9}
{"type":"dog","name":"Spike","hasBone":true}
1. 定义父类和子类
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 去反序列化成具体子类

import com.google.gson.*
import java.lang.reflect.Type
class AnimalDeserializer : JsonDeserializer {
    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(obj, Cat::class.java)
            "dog" -> context.deserialize(obj, Dog::class.java)
            else  -> throw JsonParseException("unknown type: $type")
        }
    }
}

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

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

4.1 单个对象

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)

val jsonList = """
[
  {"type":"cat","name":"Tom","lives":9},
  {"type":"dog","name":"Spike","hasBone":true}
]
""".trimIndent()
val listType = object : com.google.gson.reflect.TypeToken>() {}.type
val animals: List = 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

例如:

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 = 多态序列化无痛方案