如何将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
属性是通过匕首注入的,我需要在onDestroyView
中将它们设置为null
– 或者有内存泄漏。 据我所知,这在kotlin中是不可能的(没有反思)。 我可以使这些属性为空,但是这将会破坏Kotlin无效安全的目的。
我不太清楚如何解决这个问题。 理想情况下,可能会有一些类型的注释处理器会生成java代码,以便在onDestroyView
自动清除特定变量?
Kotlin lateinit
属性使用null
作为未初始化的标志值,并且没有干净的方式在lateinit
属性的后台字段中设置null
而不进行反射。
但是,Kotlin允许您使用委托属性来覆盖属性行为。 好像没有允许在kotlin-stdlib
委托,但是如果你kotlin-stdlib
需要这个行为,你可以实现你自己的委托去做,在你的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 } }
用法:
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
,但是IMO在这里是合适的,因为在你的程序的任何一点它都可以是null
。