首页 文章

在Kotlin中,如何使用零构造函数参数声明数据类?

提问于
浏览
9

假设我想为整数列表声明一个简单的代数数据类型:

sealed class IntList
data class Cons(val head: Int, val tail: IntList): IntList()
data class Nil() : IntList()

但是,最后一个声明会导致错误

数据类必须至少有一个主构造函数参数

  • 为什么会出现此限制?查看文档,似乎没有很好的技术理由要求数据类构造函数是非空的 .

  • 是否可以表示无需构造函数而无需编写大量的样板代码?如果我将最后一个声明更改为类似的

sealed class Nil() : IntList()

然后我失去了 hashCode()equals() 的免费实现,它们是 data class 声明免费提供的 .

EDIT

Alex Filatov给出了一个很好的简短解决方案 . 显然,你永远不需要多个 Nil 的实例,所以我们可以只定义一个单例对象

object Nil : IntList()

但是,如果我们的列表由类型参数参数化,我们会怎么做?也就是说,现在我们定义的前两行是

sealed class List<A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()

我们不能声明从 List<A> 派生的任何 A 的多态单例 Nil 对象,因为我们必须在声明时为 A 提供具体类型 . 解决方案(取自this post)是将 A 声明为协变类型参数,并将 Nil 声明为 List<Nothing> 的子类型,如下所示:

sealed class List<out A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()
object Nil : List<Nothing>()

这允许我们写

val xs: List<Int> = Cons(1, Cons(2, Nil))
val ys: List<Char> = Cons('a', Cons('b', Nil))

3 回答

  • 12

    你将不得不创建一个通常的类

    class Nil : IntList()
    

    并自己实施 hashCode()equals() .

    没有字段的数据类没有任何意义,因为它的工作是表示数据 .


    或者:您可以使用一个对象类(如Alex Filatov所述),它是一个单实例类 . 因为,每个单独的 Nil 实例都不需要状态,所以它们可以共享一个 .

  • -2

    如果您真的想要源代码的一致性,可以使用默认值来实现最小的数据类型 .

    data class Nil(val _u: Byte = 0) : IntList()
    

    要么

    data class Nil(val _u: Nothing? = null) : IntList()
    
  • 0

    因为 data class 没有数据没有意义 . 使用 object 作为单身人士:

    object Nil : IntList()
    

相关问题