Kotlin 等效于省略 TypeScript 实用程序类型

Kotlin equivalent of Omit TypeScript utility type

我在 Kotlin 中有两个非常相似的数据 类。唯一的区别是一个有ID字段,另一个没有(如果模型只存储在数据库中,则会生成ID)。

data class RouteWithId(
    val id: String,
    val name: String,
    val description: String,
    val comments: List<Comment>,
    val media: List<Media>,
    val points: List<RoutePoint>,
    val userId: String,
    val status: RouteState,
    val tracks: List<TrackInfo>,
) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
}
data class Route(
    val name: String,
    val description: String,
    val comments: List<Comment>,
    val media: List<Media>,
    val points: List<RoutePoint>,
    val userId: String,
    val status: RouteState,
    val tracks: List<TrackInfo>,
) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
}

我试图用一个可为空的 id 字段统一它们,但很多时候,它只会产生很多噪音,因为我知道在几个地方,ID 会存在(从数据库加载后的任何后处理) .

我看到 TypeScript 有一个名为 Omit 的 utility type ,它可以用来从另一个类型派生出一个类型但省略了一些字段。

这对我的用例来说是完美的,因为我知道从数据库加载后该字段将存在,而在此之前我也知道它不会存在。

你知道如何在 Kotlin 中实现同样的事情吗?

Kotlin 中没有联合类型,但您可以使用继承来实现:

class Route(val name: String, val description: String, /* ... */) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
    
    fun withId(id: Int): DBRoute = DBRoute(id, name, description, /* ... */)
}

class DBRoute(val id: Int, name: String, description: String, /* ... */) : Route(name, description) {    
    fun asRoute(): Route = Route(name, description, /* ... */)
}

这样,您可以将 Route 转换为 DBRoute,反之亦然。这样做的缺点是您必须在 DBRoute.

的构造函数中编写 Route 中的所有字段

我不知道它是否适合您,但您也可以使用 Map<Int, Route> 而无需创建新的 class,但这更适合 class 如果你需要一些操作来以某种方式包含 ID。

TLDR:您无法在 Object-Oriented 语言(如 Kotlin.

中“正确地”实现您想要的

答案:

首先,让我们回顾一下 Object-Oriented 语言的原则之一:继承。 (See here for full ist of principles)

Inheritance. Relationships and subclasses between objects can be assigned, allowing developers to reuse a common logic while still maintaining a unique hierarchy. This property of OOP forces a more thorough data analysis, reduces development time and ensures a higher level of accuracy.

因此,根据继承的定义,subclass 没有选择继承什么的选项。这意味着,它必须继承其 parent class 的所有属性;它不能对继承什么挑剔。

例如,如果 parent class 有一个 Lung Cancer 属性,child class 也必须有 Lung Cancer 属性。 child class 不能说“呃,Lung Cancer 不好,所以我不想继承它!”。

其次,我们参考Kotlin中Data class的定义。 (Definition of Data Class) Kotlin 在 Data class 中设置了一个非常严格的规则,这样“数据 class 不允许有 child class 和 parent class”。这意味着,当您使用 Data Class 时,继承不适合。遗憾的是,如果你想在 Kotlin 中进行继承,你不能使用 Data Class,只需使用普通的 class.

然而,如果你真的必须这样做,你可以通过 OOP“作弊”并利用 Kotlin 中的 abstractinterface classes。 Apparent够了,这在 OOP 中是一个非常糟糕的做法。我建议您再次重新考虑您的建模系统。 (你考虑过在 kotlin 中使用 Optional 吗?)

这是我给你的例子:

//create an interface class that 'enforces ID'
interface HasID {
    val id: Int
}

//Data Class must have ID
data class RouteWithId(val length: Int, override val id: Int) : HasID

//Data Class without ID
data class RouteWithOutId(val name: String)

如果您还有其他问题,请告诉我。谢谢