【问题标题】:Kotlin: Can an abstract super class have an abstract constructor?Kotlin:抽象超类可以有抽象构造函数吗?
【发布时间】:2017-09-08 14:26:28
【问题描述】:

我刚刚写了这个,就目前而言还不错:

import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.int
import com.github.salomonbrys.kotson.jsonObject
import com.google.gson.JsonElement
import com.google.gson.JsonObject

abstract class BatchJobPayload {
    abstract fun toJson(): JsonObject
}

class BookingConfirmationMessagePayload(val bookingId: Int) : BatchJobPayload() {
    constructor(payload: JsonElement) : this(payload["bookingId"].int)

    override fun toJson() = jsonObject(
        "bookingId" to bookingId
    )
}

但我想坚持,如果可能的话,所有扩展 BatchJobPayload 的类都实现一个带有签名的辅助构造函数 constructor(payload: JsonElement): BatchJobPayload,用于反序列化。

BookingConfirmationMessagePayload 有这样的构造函数只是因为我把它放在那里,而不是因为BatchJobPayload 坚持它...

【问题讨论】:

  • 我不认为你可以这样做..如果你给BatchJobPayload一个构造函数,所有子类都必须调用这个超级构造函数,但我认为它没有帮助
  • 强制两个构造函数是没有意义的。如果您希望有人总是通过bookingId,为什么不完全删除默认构造函数呢?那它有什么作用呢? Insead 为它设置了一个默认值,也许吧?
  • @Sourabh 的目的是始终有 两种 方法来创建BatchJobPayload 的子类:直接使用主构造函数并间接使用当你只有一个 JSON 字符串时是在现有实例上调用 toJson 的结果。见serialization。

标签: kotlin


【解决方案1】:

我想出了一个可行的选项如下:

interface BatchJobPayload {
    fun toJson(): JsonObject
}

interface BatchJobPayloadDeserialize {
    operator fun invoke(payload: JsonElement): BatchJobPayload
}

class BookingConfirmationMessagePayload(val bookingId: Int) : BatchJobPayload {
    override fun toJson() = jsonObject(
        "bookingId" to bookingId
    )
}

class BookingConfirmationMessagePayloadDeserialize : BatchJobPayloadDeserialize {
    override operator fun invoke(payload: JsonElement) =
        BookingConfirmationMessagePayload(payload["bookingId"].int)
}

现在您可以从 JsonElement 反序列化 BookingConfirmationMessagePayload 对象,如下所示:

BookingConfirmationMessagePayloadDeserialize()(payload)

(invoke 运算符在这里只是一些语法糖,可能与钝角接壤......)

实际上我还是更喜欢不那么冗长的原始代码 --- 未来需要子类化 BatchJobPayload 的开发人员最初可能会忽略定义一个采用 JsonElement 的构造函数,但一旦他们肯定会意识到他们的遗漏只有一个 JSON 字符串,他们需要将其转换为新类的实例......

【讨论】:

    【解决方案2】:

    您不能强制使用超级构造函数,但可以让工厂强制执行 spawn 方法,该方法返回 BatchJobPayload 的子类,这使您可以确保类是可构造的。

    看起来像这样:

    class JsonObject // Included to make compiler happy
    
    abstract class Factory<T> {
        abstract fun make(obj: JsonObject): T
    }
    
    abstract class Base {
        abstract fun toJson(): JsonObject
    }
    
    
    class A(val data:JsonObject):Base() {
        override fun toJson(): JsonObject {
            return JsonObject()
        }
    }
    
    class AFactory: Factory<A>() {
        override fun make(obj: JsonObject): A {
            return A(obj)
        }
    
    }
    
    fun main(args: Array<String>) {
        val dummyJson = JsonObject()
    
        var factory = AFactory()
        var instance = factory.make(dummyJson)
    
        println(instance)
    }
    

    【讨论】:

    • 我无法让这段代码工作。我不确定你是否可以使用上面显示的类型参数在 Koltin 中构造一个对象:return A(obj)...
    • @gtod 我添加了一个 main 方法来展示如何使用它,它在我的端成功运行,你面临什么样的问题?
    • 对不起@jrtapsell,你是对的,我让它这样工作:
    • 没问题,我应该从一开始就包含main方法
    • 其实我改了defn。 A 到 class A(val id: Int) : Base() { 和 AFactory 的覆盖现在是 override fun make(obj: JsonObject) = A(obj["id"].int)。这是可行的,但是因为通常A、B 等类将需要它们自己独特的构造函数,类型参数的意义何在(你看到我自己的答案没有使用)?
    猜你喜欢
    • 2010-09-20
    • 2019-06-02
    • 2020-11-27
    • 1970-01-01
    • 2023-03-04
    • 2022-01-22
    • 1970-01-01
    • 2016-05-05
    相关资源
    最近更新 更多