如何为具有许多不可变属性的Kotlin数据类创建构建器

时间:2017-08-06 18:36:35

标签: kotlin builder-pattern

我有一个Kotlin数据类,我正在构建许多不可变属性,这些属性是从单独的SQL查询中获取的。如果我想使用构建器模式构造数据类,如何在不使这些属性可变的情况下执行此操作?

例如,而不是构建通过

var data = MyData(val1, val2, val3)

我想用

builder.someVal(val1)
// compute val2
builder.someOtherVal(val2)
// ... 
var data = builder.build()

同时仍使用Kotlin的数据类功能和不可变属性。

4 个答案:

答案 0 :(得分:3)

我不认为Kotlin有本土建设者。您始终可以计算所有值并在最后创建对象。

如果您仍想使用构建器,则必须自行实现。查看this question

答案 1 :(得分:3)

我同意Grzegorz答案中的数据副本块,但它与使用构造函数创建数据类的语法基本相同。如果你想使用那种方法并保持一切清晰,你可能会事先计算所有内容并最终将所有值一起传递。

要拥有更像建筑师的东西,您可以考虑以下事项:

我们说你的数据类是

data class Data(val text: String, val number: Int, val time: Long)

您可以创建一个这样的可变构建器版本,使用构建方法来创建数据类:

class Builder {
    var text = "hello"
    var number = 2
    var time = System.currentTimeMillis()

    internal fun build()
            = Data(text, number, time)

}

除了像这样的构建器方法:

fun createData(action: Builder.() -> Unit): Data {
    val builder = Builder()
    builder.action()
    return builder.build()
}

Action是一个可以直接修改值的函数,createData会在之后直接为它构建一个数据类。 这样,您可以使用以下命令创建数据类:

val data: Data = createData {
    //execute stuff here
    text = "new text"
    //calculate number
    number = -1
    //calculate time
    time = 222L
}

每个说法没有setter方法,但您可以直接为可变变量分配新值,并在构建器中调用其他方法。

你也可以通过为每个变量指定你自己的函数来使用kotlin的get和set,这样它就可以做更多的事情而不是设置字段。

也不需要返回当前的构建器类,因为您始终可以访问其变量。

补充说明:如果您在意,createData可以缩短为:

fun createData(action: Builder.() -> Unit): Data = with(Builder()) { action(); build() }.
  

"使用新的构建器,应用我们的操作并构建"

答案 2 :(得分:1)

不需要在Kotlin中创建自定义构建器 - 为了实现类似构建器的语义,您可以使用copy方法 - 它非常适合您想要获取对象的情况。副本稍有改动。

data class MyData(val val1: String? = null, val val2: String? = null, val val3: String? = null)

val temp = MyData()
  .copy(val1 = "1")
  .copy(val2 = "2")
  .copy(val3 = "3")

或者:

val empty = MyData()
val with1 = empty.copy(val1 = "1")
val with2 = with1.copy(val2 = "2")
val with3 = with2.copy(val3 = "3")

由于您希望所有内容都是不可变的,因此必须在每个阶段进行复制。

此外,只要构造的结果是不可变的,在构建器中具有可变属性就没有问题。

答案 3 :(得分:1)

可以使用注释处理器机械化创建构建器类。  我刚刚创建ephemient/builder-generator来证明这一点。

请注意,目前,kapt适用于生成的Java代码,但生成的Kotlin代码存在一些问题(请参阅KT-14070)。出于这些目的,这不是一个问题,只要将可空性注释从原始Kotlin类复制到生成的Java构建器(使得使用生成的Java代码的Kotlin代码看到可空/非可空类型而不是只是平台类型)。