我正在按照以下示例调查 Kotlin DSL:-
https://github.com/zsmb13/VillageDSL
我对如何对 DSL 公开的所有属性实施使用规则感兴趣。
以下面的例子为例:-
val v = village {
house {
person {
name = "Emily"
age = 31
}
person {
name = "Jane"
age = 19
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想强制执行一个规则,阻止 DSL 的用户能够输入重复的属性,如下所示
val v = village {
house {
person {
name = "Emily"
name = "Elizabeth"
age = 31
}
person {
name = "Jane"
age = 19
age = 56
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经尝试过使用 Kotlin 合同,例如
contract { callsInPlace(block, EXACTLY_ONCE) }
Run Code Online (Sandbox Code Playgroud)
但是,这些只允许在顶级函数中使用,并且在遵循 DSL 中的 Builder 模式时,我看不到如何使用合同,例如
@SimpleDsl1
class PersonBuilder(initialName: String, initialAge: Int) {
var name: String = initialName
var age: Int = initialAge
fun build(): Person {
return Person(name, age)
}
}
Run Code Online (Sandbox Code Playgroud)
是否可以达到我想要的每个人只强制设置一个属性的效果?
不幸的是,您不能使用合同来获取您正在寻找的编译错误。我不认为它们是为了你在这里绑的目的......但我可能是错的。对我来说,它们是向编译器提示可空性和不变性之类的东西。即使您可以按照自己的意愿使用它们,我也不认为您会遇到您正在寻找的编译错误。
但是第二个解决方案是在运行时有一个异常。属性委托可以为此提供一个很好的可重用解决方案。这是对您的示例进行了一些修改。
class PersonBuilder {
var name: String? by OnlyOnce(null)
var age: Int? by OnlyOnce(null)
fun build(): Person {
name?.let { name ->
age?.let { age ->
return Person(name, age)
}
}
throw Exception("Values not set")
}
}
class OnlyOnce<V>(initialValue: V) {
private var internalValue: V = initialValue
private var set: Boolean = false
operator fun getValue(thisRef: Any?, property: KProperty<*>): V {
return internalValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
if (set) {
throw Exception("Value set already")
}
this.internalValue = value
this.set = true
}
}
fun person(body: PersonBuilder.() -> Unit) {
//do what you want with result
val builder = PersonBuilder()
builder.body()
}
fun main() {
person {
name = "Emily"
age = 21
age = 21 // Exception thrown here
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
259 次 |
| 最近记录: |