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

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

问题描述

所以我将 kotlin 用于 android,并且在膨胀视图时,我倾向于执行以下操作:

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) }

这个方法行得通.但是,在某些情况下,它会导致应用程序出错.如果这是一个fragment,并且这个fragment进入backstack,onCreateView将被再次调用,并且fragment的视图层次结构将被重新创建.这意味着,惰性启动的 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 fragment empty recycler view after返回

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

推荐答案

这里是一个可重置懒惰的快速版本,它可以更优雅,需要仔细检查线程安全,但这基本上就是这个想法.您需要一些东西来管理(跟踪)懒惰的委托,以便您可以调用重置,然后可以管理和重置的东西.这将 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

resettable lazy 的实现:

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天全站免登陆