The below class has a very unique lifecycle, which requires me to temporarily null out lateinit
properties
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
} }
Here's how it's used by the framework.
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
My lateinit
properties are injected by dagger, and I need to set them to null
in onDestroyView
- or have a memory leak. This however is not possible in kotlin, as far as I am aware (without reflection). I could make these properties nullable, but that would defeat the purpose of Kotlin's null safety.
I'm not quite sure how to solve this. Ideally there could be some type of annotation processor that would generate java code to null out specific variables automatically in onDestroyView
?
While using Lateinit , the variable can't be of the null type. Lateinit cannot be used for primitive datatypes, i.e., Long and int. If you try accessing Lateinit variables without initializing, it will throw an exception stating that it is not initialized or properly being accessed.
lateinit var is only for first time initialization, for cases when you know that your variable is not nullable but you cannot initialize it in constructor(for example in android framework). There is no point in having lateinit var if you "uninitialize" it after. Kotlin does not allow this kind of manipulation.
In order to avoid this situation, Kotlin introduced a new modifier called as "lateInit". Along with this modifier, Kotlin provides a couple of methods to check whether this variable is initialized or not. Use "lateInit" with a mutable variable. That means, we need use "var" keyword with "lateInit".
Kotlin lateinit
properties use null
as an uninitialized flag value, and there's no clean way to set null
in the backing field of a lateinit
property without reflection.
However, Kotlin allows you to override the properties behavior using delegated properties. Seems like there's no delegate that allows that in kotlin-stdlib
, but if you need exactly this behavior, you can implement your own delegate to do that, adding some code to your utils:
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 }
}
And the usage:
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()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With