如何在 Kotlin 中结合 Kotlinx 序列化正确使用类继承

问题描述

我有一个包含以下内容的简单层次结构:

  • 抽象类 BaseItem
  • 打开类项目:BaseItem
  • 类背包:物品

它们都应该与 Kotlinx 序列化一起使用。一切顺利,直到我添加Backpack 类。 我使用 Kotlinx 序列化的 1.4.32 版本。

这是我的类层次结构的详细信息

// Items.kt

@Serializable
sealed class BaseItem {
    abstract val id: String
    abstract val type: ItemType
    abstract var brand: String
    abstract var model: String
    abstract var imageLink: String
    abstract var traits: MutableList<Trait>
    abstract var implicitTraits: MutableList<Trait>
    abstract var details: MutableMap<String,String>
}

@Serializable
open class Item(
    override val id: String = UUID.randomUUID().toString(),override val type: ItemType = ItemType.UNDEFINED,override var brand: String,override var model: String,override var imageLink: String,override var traits: MutableList<Trait>,override var implicitTraits: MutableList<Trait>,override var details: MutableMap<String,String>,) : BaseItem()

@Serializable // is marked as incorrect 
class Backpack(
    brand: String,model: String,imageLink: String,traits: MutableList<Trait>,implicitTraits: MutableList<Trait>,details: MutableMap<String,var volume: Int
) : Item(
    type = ItemType.BACKPACK,brand = brand,model = model,imageLink = imageLink,traits = traits,implicitTraits = implicitTraits,details = details
)

IDE 向我显示了附加到 @Serialization 类的 Backpack 注释的以下消息。

This class is not serializable automatically because it has primary constructor parameters that are not properties

我无法找出使此正确的工作方式

解决方法

这是因为你的构造函数的参数没有定义为类的属性。要将参数定义为属性,您必须向参数添加 valvar。这将解决您当前的错误消息:

@Serializable
class Backpack(
    override var brand: String,override var model: String,override var imageLink: String,override var traits: MutableList<Trait>,override var implicitTraits: MutableList<Trait>,override var details: MutableMap<String,String>,var volume: Int
) : Item(
    type = ItemType.BACKPACK,brand = brand,model = model,imageLink = imageLink,traits = traits,implicitTraits = implicitTraits,details = details
)

但这仍然无法编译,因为对于两个类中使用的所有属性,您最终都会得到 Serializable class has duplicate serial name of property 'brand',either in the class itself or its supertypes。但无论如何,我对设计有点惊讶,因为从非抽象类继承通常不是一个好习惯。在不知道意图的情况下,我想知道这样对你是否也不起作用:

sealed class BaseItem {
    abstract val id: String
    abstract val type: ItemType
    abstract var brand: String
    abstract var model: String
    abstract var imageLink: String
    abstract var traits: MutableList<Trait>
    abstract var implicitTraits: MutableList<Trait>
    abstract var details: MutableMap<String,String>
}

@Serializable
data class Item(
    override val id: String = UUID.randomUUID().toString(),override val type: ItemType = ItemType.UNDEFINED,override var brand: String,) : BaseItem()

@Serializable
data class Backpack(
    override val id: String = UUID.randomUUID().toString(),override val type: ItemType = ItemType.BACKPACK,override var var volume: Int
) : BaseItem()

顺便说一句。从 @Serializable 中删除了 BaseItem,因为它是不必要的,因为无论如何该类是抽象的,因此根本不会被序列化。我还制作了您的类 data class,因为我的印象是那些基本上是用来保存数据的,而那些通常是用 data class 实现的。我留下了我看到的许多 var,因为我不知道这些的原因,但我这边的一个小提示是,你应该更喜欢 val 而不是 var,尤其是在 {{1} }.在这种情况下,data class 对我来说就像是一种代码气味,您可能需要考虑使用 var。 Kotlin 页面本身就是这类事情的一个很好的文献:https://kotlinlang.org/docs/coding-conventions.html#idiomatic-use-of-language-features

,

来自Designing Serializable Hierarchy

要使类的层次结构可序列化,必须将父类中的属性标记为抽象,从而使 [父] 类也为抽象。