【问题标题】:Kotlin equivalent of Omit TypeScript utility typeKotlin 等效于省略 TypeScript 实用程序类型
【发布时间】:2021-06-16 19:42:45
【问题描述】:

我在 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 中实现同样的目标吗?

【问题讨论】:

  • 为什么不简单地将 ID 设置为 -1 ?

标签: typescript kotlin data-class


【解决方案1】:

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&lt;Int, Route&gt; 而无需创建新类,但如果您需要一些操作来包含 ID,这将更适合类不知何故。

【讨论】:

  • 嘿@enzo,我相信 Kotlin 不可能做到这一点,因为 data class 不能继承 data class。简而言之,data class 的父类或子类不能是 data class
  • @minchaej 你是对的,我已经更新了我的答案。
【解决方案2】:

TLDR:您无法在像Kotlin 这样的面向对象语言中“正确”实现您想要的。

答案:

首先,让我们回顾一下面向对象语言的一个原则:继承。 (See here for full ist of principles)

继承。可以分配对象之间的关系和子类,从而允许开发人员重用通用逻辑,同时仍保持唯一的层次结构。 OOP 的这一特性可强制进行更彻底的数据分析,缩短开发时间并确保更高的准确性。

因此,通过继承的定义,子类没有选择继承什么的选项。这意味着,它必须继承其父类的所有属性;它不能挑剔继承什么。

例如,如果父类具有Lung Cancer 属性,则子类也必须具有Lung Cancer 属性。子类不能说“呃,Lung Cancer 不好,所以我不想继承它!”。

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

但是,如果你真的必须这样做,你可以通过 OOP “作弊”并利用 Kotlin 中的 abstractinterface 类。显然,这在 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)

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

【讨论】:

  • “所以通过继承的定义,子类没有选择继承什么的选项。”这只意味着Route 不能扩展RouteWithId,但是你为什么会期望它呢?
  • 嘿@AlexeyRomanov,继承和Data class 是两个不同的主题。在这种情况下,RouteRouteWithIdData classes,因此它们不符合继承条件(Kotlin 就是这样),因此“Route can't extend RouteWithId”是正确的。另一方面,在 OOP 中,当您继承(或扩展或子类)某些东西时,您必须获取父级的所有属性。所以在继承中,你不能对继承什么挑剔。如果您有任何问题,请告诉我。
猜你喜欢
  • 2020-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 2019-05-26
  • 2023-02-03
  • 2021-07-06
  • 1970-01-01
相关资源
最近更新 更多