【问题标题】: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 中的 abstract 和 interface 类。显然,这在 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 是两个不同的主题。在这种情况下,Route 和RouteWithId 是Data 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
相关资源
最近更新 更多