Kotlin lazy usage

2019-06-16 11:24发布

问题:

I'm currently using Realm in my application, and to make sure i manage Realm instances correctly, i introduced in my base Activity a variable like this:

protected val realm: Realm by lazy {
        Realm.getDefaultInstance()
}

And then in onDestroy i do this:

override fun onDestroy() {
    super.onDestroy()
    realm.close()
}

And then i realised this is a waste. If the current activity doesn't use realm, it will open and immediately close in onDestroy.

So i updated to this:

    private var usedRealm = false

    protected val realm: Realm by lazy {
        usedRealm = true
        Realm.getDefaultInstance()
    }

override fun onDestroy() {
        super.onDestroy()

        if (usedRealm) {
            realm.close()
        }
    }

Is there any way to accomplish the same, without the extra flag?

回答1:

  1. There is a bug in your current implementation. If Realm.getDefaultInstance() throws then usedRealm will be set to true but the lazy won't actually be initialized (intialization would have failed). You can fix this by only calling usedRealm = true after calling Realm.getDefaultInstance():

    protected val realm: Realm by lazy {
        val realm = Realm.getDefaultInstance()
        usedRealm = true
        realm
    }
    

    or

    protected val realm: Realm by lazy {
        Realm.getDefaultInstance().apply { usedRealm = true }
    }
    
  2. You can accomplish the same without the extra flag by keeping a reference to the raw Lazy object itself:

    private val lazyRealm = lazy { Realm.getDefaultInstance() }
    
    protected val realm by lazyRealm
    
    override fun onDestroy() {
        super.onDestroy()
    
        if (lazyRealm.isInitialized()) {
            realm.close()
        }
    }
    

    This still requires an additional field but you no longer have to maintain the initialization state yourself.

  3. You can also use Lazy directly instead of as a delegate:

    protected val lazyRealm = lazy { Realm.getDefaultInstance() }
    
    override fun onDestroy() {
        super.onDestroy()
    
        if (lazyRealm.isInitialized()) {
            lazyRealm.value.close()
        }
    }
    

    or

    protected val lazyRealm = lazy { Realm.getDefaultInstance() }
    
    override fun onDestroy() {
        super.onDestroy()
    
        with(lazyRealm) {
            if (isInitialized()) {
                value.close()
            }
        }
    }
    

    This makes it so that there is no extra property but Lazy is now part of your API and everywhere where you would have simply referenced realm you now have to reference lazyRealm.value. You get to weigh the pros and cons. :-)