为什么需要「多态对象」?
多个数据类型 共享一个父类型 时,我们希望都能用 同一种方式 序列化/反序列化。
现实类比:
比如消息系统里同属于 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有两个子类:
Cat、Dog
后端返回的 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 多态:
JSON 里要有个
"type"字段(你自己约定)写一个
JsonDeserializer<父类>/TypeAdapter<父类>在里面:
读
"type"
when (type)分支判断
context.deserialize(...)解析成对应的子类
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 = 多态序列化无痛方案