Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between get() and by lazy

Having a room Dao as below,

@Dao
public abstract class AccountDao {
    @Query("SELECT * FROM Account LIMIT 0,1")
    public abstract Account readAccount();
}

is there any differences between get() and by lazy in the sample below?

open val account: LiveData<Account>
        get() = accountDao.readAccount()
open val account: LiveData<Account> by lazy { accountDao.readAccount() }
like image 539
You Qi Avatar asked Aug 06 '19 14:08

You Qi


2 Answers

The difference is in how many times the function body (accountDao.readAccount()) will be executed.

The lazy delegate will execute the lambda one single time the first time it is accessed and remember the result. If it is called again, that cached result is returned.

On the other hand, defining the getter (get()) will execute the function body every time, returning a new result every time.

For example, let's suppose we have a class called Foo with both a getter and a lazy value:

class Foo {
    val getterVal: String
        get() = System.nanoTime().toString()

    val lazyVal: String by lazy { System.nanoTime().toString() }
}

And then use it:

fun main() {
    with(Foo()) {
        repeat(2) {
            println("Getter: $getterVal")
            println("Lazy:   $lazyVal")
        }
    }
}

For me, this prints:

Getter: 1288398235509938
Lazy:   1288398235835179
Getter: 1288398235900254
Lazy:   1288398235835179

And we can see that the getter returns a newly calculated value each time, and the lazy version returns the same cached value.

like image 156
Todd Avatar answered Oct 02 '22 04:10

Todd


In addition to Todd's answer:

Yes, there is a difference for LiveData objects as well. Every call of accountDao.readAccount() will result in a different LiveData object. And it does matter, despite the fact that all of the returned LiveData will get updated on every change in the Account entity. Let me explain on these examples:

  • by lazy

As Todd mentioned, the block inside the lazy delegate will be executed once, at the first time that the account property is accessed, the result will be cached and returned on every next access. So in this case a single one LiveData<Account> object is created. The bytecode generated by Kotlin to achieve this is equivalent to this in Java:

public class Activity {

    private Lazy account$delegate

    public LiveData<Account> getAccount() {
        return account$delegate.getValue();
    }
}
  • get()

By creating a custom account property's getter and calling accountDao.readAccount() inside, you will end up with different LiveData<Account> objects on every access of the account property. Once more, bytecode generated for this case in Kotlin in Java is more or less this:

public class Activity {

    public LiveData<Account> getAccount() {
        return accountDao.readAccount();
    }
}

So you can see, using a lazy property results in generating a backing field for this property, while using a custom getter creates a wrapper method for the accountDao.readAccount() call.

It's up to your needs which approach you should use. I'd say that if you have to obtain the LiveData only once, you should go with get(), because a backing field is needless in that case. However if you're going to access the LiveData in multiple places in your code, maybe a better approach would be to use by lazy and create it just once.

like image 41
jsamol Avatar answered Oct 02 '22 05:10

jsamol