实施带有可空值的类型安全的类层次结构

时间:2019-06-04 16:01:40

标签: kotlin

I(通常)具有一个具有两个状态的资源,即创建前和创建后两个状态,其中两个状态除了id字段外,都具有相同的字段。 id在创建前的状态为null,在创建后的状态为null。

我想以一种干净且类型安全的方式定义和使用此资源。

通常将此ID字段表示为可为null的字段,这可以在类定义中以最少的样板处理两种情况。问题在于它在业务逻辑中创建了很多样板,因为您无法通过查看资源的类型来断言资源是预先创建还是之后创建。

以下是可为空方法的示例:

data class Resource(val id: String?, val property: String)

这很容易定义,但由于缺乏编译时保证,因此处理起来并不简单。

下面是一个更加类型安全的方法的示例:

sealed class Resource(val property: String) {
  class WithoutID(property: String): Resource(property)
  class WithID(val id: String, property: String): Resource(property)
}

这使我可以绕过Resource.WithIDResource.WithoutID,除了id以外,它们具有相同的字段和方法。

这种类型安全的方法的不便之处在于,当您有许多property字段时,资源定义代码会变得肿。这种膨胀使代码更难阅读。

我想知道是否有另一种方法可以减少样板,或者Kotlin是否具有使这种事情更简单的功能。

2 个答案:

答案 0 :(得分:1)

如何定义

sealed class MayHaveId<T> { abstract val record: T }
class WithId<T>(val id: String, override val record: T): MayHaveId<T>()
class WithoutId<T>(override val record: T): MayHaveId<T>()

class Resource(val property: String)
// and other similar types

并使用WithId<Resource>WithoutId<Resource>?在Scala中,您可以添加从MayHaveId<T>T的隐式转换,但是不能在Kotlin ,,,也不能编写: T by record。仍然应该干净到可以使用。

答案 1 :(得分:0)

其中一种选择是依靠接口内部的properties进行合成。

interface Resource {
    val property: String
}

interface WithId : Resource {
    val id: Int
}

interface WithOtherField : Resource {
    val otherField: Any
}


class WithoutIdImpl(override val property: String) : Resource
class WithIdImpl(override val id: Int, override val property: String) : WithId
class WithIdAndOtherField(
        override val id: Int,
        override val otherField: Any,
        override val property: String) : WithId, WithOtherField

我没有从您的示例中了解到您如何在Resource的两种状态之间切换。因此可能有一个需要克服的空白。 聪明的转换可能会允许切换状态。