web-dev-qa-db-ja.com

lateinitKotlinプロパティを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プロパティは短剣によって挿入され、nullonDestroyViewに設定する必要があります。そうしないと、メモリリークが発生します。ただし、これは、私が知る限り(反省なしで)、kotlinでは不可能です。これらのプロパティをnull可能にすることはできますが、それではKotlinのnull安全性の目的が無効になります。

これを解決する方法がよくわかりません。理想的には、onDestroyViewで特定の変数を自動的に無効にするJavaコードを生成する、ある種の注釈プロセッサが存在する可能性がありますか?

13
ZakTaccardi

Kotlin lateinitプロパティは初期化されていないフラグ値としてnullを使用し、リフレクションなしでnullプロパティのバッキングフィールドにlateinitを設定するクリーンな方法はありません。


ただし、Kotlinでは、委任されたプロパティを使用してプロパティの動作をオーバーライドできます。 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()
    }
}
7
hotkey

必要なのは、nullableの狂気のない通常の健全なlateinitプロパティだと思います。

class SalesController : BaseController, SalesView {
    @Inject @JvmField var viewBinder: SalesController.ViewBinder? = null

このソリューションでは、コンパイラはviewBindernullであるかどうかを確認するように求めますが、IMOは、プログラムの任意の時点でnullになる可能性があるため、ここでは適切です。

0
voddan