Можно-ли в kotlin делегировать свойство другому свойству, если оно так же делегировано?
Игрался я тут с ORM exposed и столкнулся с одной проблемой, при реализации наследования, через агрегацию. Если, например, написать так (пример совершенно выдуманный):
data class AEntity(val _id : EntityID<UUID>) : UUIDEntity(_id) {
companion object : UUIDEntityClass<AEntity>(ATable)
val b by BEntity optionalBackReferencedOn BTable.id
var text1 : String by ATable.text1
override fun toString(): String {
return "AEntity(_id=$_id, text1='$text1')"
}
}
data class BEntity(val _id : EntityID<UUID>) : UUIDEntity(_id) {
companion object : UUIDEntityClass<BEntity>(BTable)
var a : AEntity by AEntity referencedOn BTable.id
var int1 : Int by BTable.int1
var text1 : String by a::text1
override fun toString(): String {
return "BEntity(_id=$_id, a=$a, int1=$int1, text1='$text1')"
}
}
То выборка экземпляров BEntity из таблицы сопровождалась ошибкой, по причине того, что поле var a : AEntity само делегировано и на момент конструирования делегировать ему ещё и var text1 : String by a::text1 не получится, так как оно банально не инициализировано.
Сам я нашёл решение только в виде такого вот хитрого делегата, который, как бы, откладывает обращение к свойству var a : AEntity до того момента, как он реально понадобится. Но такое решение я не считаю самым лучшим, ибо оно добавляет лишний холостой вызов функции.
abstract class Late<TValue, TEntity>(inline val kMutableProperty0: ()-> KMutableProperty0<TValue>) :
ReadWriteProperty<TEntity, TValue> {
override fun setValue(thisRef: TEntity, property: KProperty<*>, value: TValue) {
kMutableProperty0().setValue(thisRef, property, value)
}
override fun getValue(thisRef: TEntity, property: KProperty<*>): TValue {
return kMutableProperty0().getValue(thisRef, property)
}
}
inline fun <reified TValue, reified TEntity> late(noinline kMutableProperty0: ()-> KMutableProperty0<TValue>) =
object : Late<TValue, TEntity>(kMutableProperty0) {}
data class AEntity(val _id : EntityID<UUID>) : UUIDEntity(_id) {
companion object : UUIDEntityClass<AEntity>(ATable)
val b by BEntity optionalBackReferencedOn BTable.id
var text1 : String by ATable.text1
override fun toString(): String {
return "AEntity(_id=$_id, text1='$text1')"
}
}
data class BEntity(val _id : EntityID<UUID>) : UUIDEntity(_id) {
companion object : UUIDEntityClass<BEntity>(BTable)
var a : AEntity by AEntity referencedOn BTable.id
var int1 : Int by BTable.int1
var text1 : String by late { a::text1 }
override fun toString(): String {
return "BEntity(_id=$_id, a=$a, int1=$int1, text1='$text1')"
}
}
Если кто знает решение без этих вот танцев с бубном, поделитесь пожалуйста.
Дополнено 16.03.2021 в 11:45
У предыдущего решения нашёлся один неприятный момент - оно работало только для ReadWrite свойств. Следующий вариант, вроде бы, работает и для ReadOnly.
abstract class LateR<TValue, TEntity>(inline val kProperty0: ()-> KProperty0<TValue>) :
ReadOnlyProperty<TEntity, TValue> {
override operator fun getValue(thisRef: TEntity, property: KProperty<*>): TValue {
return kProperty0().getValue(thisRef, property)
}
}
abstract class LateRW<TValue, TEntity>(inline val kMutableProperty0: ()-> KMutableProperty0<TValue>) :
LateR<TValue, TEntity>(kMutableProperty0), ReadWriteProperty<TEntity, TValue> {
override operator fun setValue(thisRef: TEntity, property: KProperty<*>, value: TValue) {
kMutableProperty0().setValue(thisRef, property, value)
}
}
inline fun <reified TValue, reified TEntity> lateRW(noinline kMutableProperty0: ()-> KMutableProperty0<TValue>) =
object : LateRW<TValue, TEntity>(kMutableProperty0) {}
inline fun <reified TValue, reified TEntity> lateR(noinline kProperty0: ()-> KProperty0<TValue>) =
object : LateR<TValue, TEntity>(kProperty0) {}
Кстати, если кто знает, как сделать так, что бы функции lateR и lateRW можно было назвать одинаково, то, также, прошу подсказать.