Kotlin惰性属性和值重置:可重置的惰性委托 [英] Kotlin lazy properties and values reset: a resettable lazy delegate

查看:349
本文介绍了Kotlin惰性属性和值重置:可重置的惰性委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我在Android上使用kotlin,并且在放大视图时,我倾向于执行以下操作:

So I use kotlin for android, and when inflating views, I tend to do the following:

private val recyclerView by lazy { find<RecyclerView>(R.id.recyclerView) }

此方法将起作用.但是,在某些情况下,它将使应用程序出错.如果这是一个片段,并且该片段进入了堆栈,则将再次调用onCreateView,并将重新创建该片段的视图层次结构.这意味着,延迟启动的recyclerView将指出一个不再存在的旧视图.

This method will work. However, there is a case in which it will bug the app. If this is a fragment, and the fragment goes to the backstack, onCreateView will be called again, and the view hierarchy of the fragment will recreated. Which means, the lazy initiated recyclerView will point out to an old view no longer existent.

解决方案是这样的:

private lateinit var recyclerView: RecyclerView

并初始化onCreateView中的所有属性.

And initialise all the properties inside onCreateView.

我的问题是,有什么办法可以重置惰性属性,以便可以再次对其进行初始化?我喜欢所有的初始化工作都是在类的顶部完成的,这有助于使代码井井有条.在以下问题中发现了特定的问题:之后的kotlin android片段为空的回收器视图后退

My question is, is there any way to reset lazy properties so they can be initialised again? I like the fact initialisations are all done at the top of a class, helps to keep the code organised. The specific problem is found in this question: kotlin android fragment empty recycler view after back

推荐答案

这里是可重置懒惰的快速版本,它可能更优雅,并且需要仔细检查线程安全性,但这基本上是个主意.您需要管理(保持跟踪)懒惰的委托人,以便可以调用reset,然后可以管理和重置.这会将lazy()包装在这些管理类中.

Here is a quick version of a resettable lazy, it could be more elegant and needs double checked for thread safety, but this is basically the idea. You need something to manage (keep track) of the lazy delegates so you can call for reset, and then things that can be managed and reset. This wraps lazy() in these management classes.

这是您的最后一堂课的样子,例如:

class Something {
    val lazyMgr = resettableManager()
    val prop1: String by resettableLazy(lazyMgr) { ... }
    val prop2: String by resettableLazy(lazyMgr) { ... }
    val prop3: String by resettableLazy(lazyMgr) { ... }
}

然后在下次访问时让所有懒惰者恢复为新值:

lazyMgr.reset() // prop1, prop2, and prop3 all will do new lazy values on next access

可重置懒惰的实现:

class ResettableLazyManager {
    // we synchronize to make sure the timing of a reset() call and new inits do not collide
    val managedDelegates = LinkedList<Resettable>()

    fun register(managed: Resettable) {
        synchronized (managedDelegates) {
            managedDelegates.add(managed)
        }
    }

    fun reset() {
        synchronized (managedDelegates) {
            managedDelegates.forEach { it.reset() }
            managedDelegates.clear()
        }
    }
}

interface Resettable {
    fun reset()
}

class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()->PROPTYPE): Resettable {
    @Volatile var lazyHolder = makeInitBlock()

    operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE {
        return lazyHolder.value
    }

    override fun reset() {
        lazyHolder = makeInitBlock()
    }

    fun makeInitBlock(): Lazy<PROPTYPE> {
        return lazy {
            manager.register(this)
            init()
        }
    }
}

fun <PROPTYPE> resettableLazy(manager: ResettableLazyManager, init: ()->PROPTYPE): ResettableLazy<PROPTYPE> {
    return ResettableLazy(manager, init)
}

fun resettableManager(): ResettableLazyManager = ResettableLazyManager()


并进行一些单元测试以确保:

class Tester {
   @Test fun testResetableLazy() {
       class Something {
           var seed = 1
           val lazyMgr = resettableManager()
           val x: String by resettableLazy(lazyMgr) { "x ${seed}" }
           val y: String by resettableLazy(lazyMgr) { "y ${seed}" }
           val z: String by resettableLazy(lazyMgr) { "z $x $y"}
       }

       val s = Something()
       val x1 = s.x
       val y1 = s.y
       val z1 = s.z

       assertEquals(x1, s.x)
       assertEquals(y1, s.y)
       assertEquals(z1, s.z)

       s.seed++ // without reset nothing should change

       assertTrue(x1 === s.x)
       assertTrue(y1 === s.y)
       assertTrue(z1 === s.z)

       s.lazyMgr.reset()

       s.seed++ // because of reset the values should change

       val x2 = s.x
       val y2 = s.y
       val z2 = s.z

       assertEquals(x2, s.x)
       assertEquals(y2, s.y)
       assertEquals(z2, s.z)

       assertNotEquals(x1, x2)
       assertNotEquals(y1, y2)
       assertNotEquals(z1, z2)

       s.seed++ // but without reset, nothing should change

       assertTrue(x2 === s.x)
       assertTrue(y2 === s.y)
       assertTrue(z2 === s.z)
   }
}

这篇关于Kotlin惰性属性和值重置:可重置的惰性委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆