在写这篇文章的时候,我注意到我在 cmets 中引用的 Bug 已经被修复了,所以我继续沿着这条代码路径走下去才意识到存在语言限制,在底部我将包含一个示例我的意思是在 cmets 中。
这两个例子在运行时都会失败。
fun test_person() {
val village = village {
house {
person {
name = "Emily"
// ::name setTo "Emily" // Commented for 2nd example of Person
age = 31
}
person {
name = "Jane"
age = 19
}
}
house {
person {
name = "Tim"
// name = "Tom" // Will break with exception
age = 20
}
}
}
println("What is our village: \n$village")
}
适用于异常的运行时中断示例:
class Village {
val houses = mutableListOf<House>()
fun house(people: House.() -> Unit) {
val house = House()
house.people()
houses.add(house)
}
override fun toString(): String {
val strB = StringBuilder("Village:\n")
houses.forEach { strB.append(" $it \n") }
return strB.toString()
}
}
fun village(houses: Village.() -> Unit): Village {
val village = Village()
village.houses()
return village
}
class House {
val people = mutableListOf<Person>()
fun person(qualities: Person.() -> Unit) {
val person = Person()
person.qualities()
people.add(person)
}
override fun toString(): String {
val strB = StringBuilder("House:\n")
people.forEach{ strB.append(" $it \n")}
return strB.toString()
}
}
class Person {
var age by SetOnce<Int>()
var name by SetOnce<String>()
override fun toString(): String {
return "Person: { Name: $name, Age: $age }"
}
}
class SetOnce <T> : ReadWriteProperty<Any?, T?> {
private var default: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T? = default
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
if (default != null) throw Exception("Duplicate set for ${property.name} on $thisRef")
else default = value
}
}
非工作示例旨在使用 lateinit 属性来控制一次值的设置,但您不能使用引用,它必须是文字 ::foo 语法。就像我说的,我没有意识到这个错误已经修复,也不知道这是语言的限制
class Person {
lateinit var name: String
lateinit var age: Number // Number because primitives can't be lateinit
/** @throws Exception when setting a property a second time */
infix fun <T> KMutableProperty0<T>.setTo(value: T) {
val prop = getProp<T>(this.name)
if (prop.isInitialized.not()) this.set(value)
else throw Exception("Duplicate set for ${this.name}")
}
private fun <T> getProp(name: String): KMutableProperty0<T> {
return when(name) {
"name" -> ::name
"age" -> ::age
else -> throw Exception("Non-existent property: $name")
} as KMutableProperty0<T>
}
}
随着合同的成熟和规则的放松,我们可能会写出类似的东西:
@OptIn(ExperimentalContracts::class)
infix fun <T> KMutableProperty0<T>.setTo(value: T) {
contract { returns() implies this@setTo.isInitialized }
this.set(value)
}
这将使我们能够将这一切都转移到 IDE 错误中,但遗憾的是我们还没有做到。