【发布时间】:2018-06-03 13:35:19
【问题描述】:
我想在 Kotlin 中使用 type-safe builders 来表示具有不可变属性的类型。
class DataClass(val p1:String, val p2:String) {}
fun builder(buildBlock: DataClass.() -> Unit) {
return DataClass(p1 = "",p2 = "").also(block)
}
//...
builder {
p1 = "p1" // <-- This won't compile, since p1 is a val
p2 = "p2" // <-- This won't compile, since p2 is a val
}
我想到了两种解决方案来解决这个问题:
选项 1: 创建构建器类:
class DataClass(val p1: String, val p2: String) {}
class DataClassBuilder(){
lateinit var p1: String
lateinit var p2: String
fun build() = DataClass(p1, p2)
}
fun builder(buildBlock: DataClassBuilder.() -> Unit) {
return DataClassBuilder().also(block).build()
}
选项 2: 创建自定义委托以防止再次设置值:
class InitOnceDelegate: ReadWriteProperty<DTest, String> {
private var state: String? = null
override fun getValue(thisRef: DTest, property: KProperty<*>): String {
return state ?: throw IllegalStateException()
}
override fun setValue(thisRef: DTest, property: KProperty<*>, value: String) {
if (state == null) {
state = value
} else {
throw IllegalStateException("${property.name} has already been initialized")
}
}
}
class DataClass() {
var p1: String by InitOnceDelegate()
var p2: String by InitOnceDelegate()
}
fun builder(buildBlock: DataClass.() -> Unit) {
return DataClass(p1 = "",p2 = "").also(block)
}
//...
val d = builder {
p1 = "p1"
p2 = "p2"
}
d.p1 = "another value" // <-- This will throw an exception now.
选项 1 的缺点是我必须维护两个类,选项 2 的缺点是编译器将允许再次设置 DataClass 中的值,并且只会在运行时进行检查。
有没有更好的方法来解决这个问题而没有提到的缺点?
【问题讨论】:
-
我认为选项 1 是您目前能做的最好的选择,即使有必须维护构建器类的负担。此外,您可以在构建结果以引发有意义的异常时添加检查以查看属性是否已初始化。
-
为了使维护构建器类变得简单,您可以delegate all its fields to a map。仍然不理想,但消除了大部分样板代码。
标签: kotlin immutability builder