如何在Kotlin中正确使用类继承,并结合Kotlinx Serialization

7

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

  • 抽象类 BaseItem
  • 开放类 Item : BaseItem
  • 类 Backpack : Item

它们都应该与 Kotlinx Serialization 一起工作。直到我添加了 Backpack 类为止一切都很顺利。 我使用的是 Kotlinx Serialization 的版本 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, String>,
    var volume: Int
) : Item(
    type = ItemType.BACKPACK,
    brand = brand,
    model = model,
    imageLink = imageLink,
    traits = traits,
    implicitTraits = implicitTraits,
    details = details
)

IDE对附加在Backpack类上的@Serialization注解显示了以下消息。
This class is not serializable automatically because it has primary constructor parameters that are not properties

我无法弄清楚正确的工作方式是什么。
2个回答

6

这是因为你的构造函数参数没有被定义为类属性。要将参数定义为属性,你需要在参数前面添加 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
)

但是,如果两个类中都使用了相同的属性,比如brand,那么代码仍然无法编译通过,并会出现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,
    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
data class Backpack(
    override val id: String = UUID.randomUUID().toString(),
    override val type: ItemType = ItemType.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>,
    override var var volume: Int
) : BaseItem()

我把 BaseItem 中的 @Serializable 删掉了,因为这是不必要的。毕竟这个类是抽象的,所以根本不会被序列化。我还把你的类都改成了 data class,因为我的印象是,它们基本上是用来保存数据的,而且通常用 data class 实现。我保留了许多 var,因为我不知道它们的原因,但我的小提示是,在 data class 中应该优先使用 val 而不是 var,在这种情况下使用 var 感觉像是代码质量问题,你可能想考虑使用 val。有关此类事项的良好文献资料是 Kotlin 页本身:https://kotlinlang.org/docs/coding-conventions.html#idiomatic-use-of-language-features


2

来自设计可序列化层次结构

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


“Items” 不过是不打算被用作抽象的 - 代码库中已经有它的实例,应该保持原样。 - xetra11
1
在这种情况下,BackPack 无法扩展 Item 并且也无法序列化。您可能需要按照 Big_Foot1989 的建议调整类层次结构。 - dnault

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接