Kotlin: обходной путь для no lateinit при использовании настраиваемого сеттера?

В моей деятельности у меня есть поле, которое должно быть недействительным и иметь настраиваемый сеттер. Я хочу инициализировать поле в моем методе onCreate поэтому я добавил lateinit в мое объявление переменной. Но, по-видимому, вы не можете этого сделать (на данный момент): https://discuss.kotlinlang.org/t/lateinit-modifier-is-not-allowed-on-custom-setter/1999 .

Это обходные пути, которые я вижу:

  • Сделайте это способом Java. Сделать поле нулевым и инициализировать его нулевым. Я не хочу этого делать.
  • Инициализируйте поле с «экземпляром по умолчанию» этого типа. Это то, что я сейчас делаю. Но это было бы слишком дорого для некоторых типов.

Может ли кто-нибудь порекомендовать лучший способ (который не включает удаление настраиваемого сеттера)?

Замените его свойством, которым обладает свойство nullable:

  var _tmp: String? = null var tmp: String get() = _tmp!! set(value) {_tmp=value; println("tmp set to $value")} 

или таким образом, если вы хотите, чтобы это соответствовало семантике lateinit :

 var _tmp: String? = null var tmp: String get() = _tmp ?: throw UninitializedPropertyAccessException("\"tmp\" was queried before being initialized") set(value) {_tmp=value; println("tmp set to $value")} 

Это может быть достигнуто с использованием свойства поддержки (согласно ответу Павлуса); однако я предпочитаю обернуть его внутри делегата, чтобы не подвергать его воздействию контекста свойства:

 open class LateInit<T: Any> : ReadWriteProperty<Any?, T> { protected lateinit var field: T final override fun getValue(thisRef: Any?, property: KProperty<*>) = get() final override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value) open fun get() = field open fun set(value: T) { field = value } } 

Это обеспечивает стандартные getters и seters, которые можно переопределить с помощью специальной реализации:

 var upperCaseString by object : LateInit<String>() { override fun set(value: String) { field = value.toUpperCase() } } 

Однако, поскольку для этой реализации требуется расширение делегата, общий тип не может быть выведен из типа свойства. Это можно преодолеть, взяв пользовательский геттер и сеттер в качестве параметров:

 class LateInit<T: Any>(private val getter: FieldHolder<T>.() -> T = { field }, private val setter: FieldHolder<T>.(T) -> Unit = { field = it }) : ReadWriteProperty<Any?, T> { private val fieldHolder = FieldHolder<T>() override fun getValue(thisRef: Any?, property: KProperty<*>) = fieldHolder.getter() override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = fieldHolder.setter(value) class FieldHolder<T: Any> { lateinit var field: T } } 

Которые затем можно использовать следующим образом:

 private var upperCaseString: String by LateInit(setter = { field = it.toUpperCase() })