Как установить для свойства 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 в любой точке вашей программы.

Другие вопросы по тегам