Как установить для свойства lateinit Kotlin значение null
Класс ниже имеет очень уникальный жизненный цикл, который требует от меня временно обнулить lateinit
свойства
class SalesController : BaseController, SalesView {
@Inject lateinit var viewBinder: SalesController.ViewBinder
@Inject lateinit var renderer: SalesRenderer
@Inject lateinit var presenter: SalesPresenter
lateinit private var component: SalesScreenComponent
override var state = SalesScreen.State.INITIAL //only property that I want to survive config changes
fun onCreateView(): View { /** lateinit variables are set here */ }
fun onDestroyView() {
//lateinit variables need to be dereferences here, or we have a memory leak
renderer = null!! //here's the problem: throws exception bc it's a non-nullable property
} }
Вот как это используется фреймворком.
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
мой lateinit
свойства вводятся кинжалом, и мне нужно установить их null
в onDestroyView
- или есть утечка памяти. Это, однако, невозможно в kotlin, насколько я знаю (без размышления). Я мог бы сделать эти свойства обнуляемыми, но это разрушило бы цель нулевой безопасности Котлина.
Я не совсем уверен, как решить это. В идеале мог бы быть какой-то тип процессора аннотаций, который генерировал бы код Java, чтобы автоматически обнулять определенные переменные в onDestroyView
?
2 ответа
Котлин lateinit
свойства использования null
в качестве значения неинициализированного флага, и нет никакого чистого способа установить null
в области поддержки lateinit
собственность без отражения.
Тем не менее, Kotlin позволяет вам переопределить поведение свойств, используя делегированные свойства. Похоже, нет делегата, который позволяет это в kotlin-stdlib
, но если вам нужно именно это поведение, вы можете реализовать свой собственный делегат, добавив некоторый код в ваши утилиты:
class ResettableManager {
private val delegates = mutableListOf<ResettableNotNullDelegate<*, *>>()
fun register(delegate: ResettableNotNullDelegate<*, *>) { delegates.add(delegate) }
fun reset() { delegatesToReset.forEach { it.reset() } }
}
class Resettable<R, T : Any>(manager: ResettableManager) {
init { manager.register(this) }
private var value: T? = null
operator fun getValue(thisRef: R, property: KProperty<*>): T =
value ?: throw UninitializedPropertyAccessException()
operator fun setValue(thisRef: R, property: KProperty<*>, t: T) { value = t }
fun reset() { value = null }
}
И использование:
class SalesController : BaseController, SalesView {
val resettableManager = ResettableManager()
@set:Inject var viewBinder: SalesController.ViewBinder by Resettable(resettableManager)
@set:Inject var renderer: SalesRenderer by Resettable(resettableManager)
@set:Inject var presenter: SalesPresenter by Resettable(resettableManager)
fun onDestroyView() {
resettableManager.reset()
}
}
Я думаю, что вам нужно нормального здорового nullable
собственность без мэджики lateinit
:
class SalesController : BaseController, SalesView {
@Inject @JvmField var viewBinder: SalesController.ViewBinder? = null
С этим решением компилятор попросит вас проверить, viewBinder
является null
или нет, но ИМО уместно здесь, так как он может стать null
в любой точке вашей программы.