get() 和 by lazy 的区别

Difference between get() and by lazy

有房道如下,

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

下面示例中的 get()by lazy 有什么区别吗?

open val account: LiveData<Account>
        get() = accountDao.readAccount()
open val account: LiveData<Account> by lazy { accountDao.readAccount() }

区别在于函数体(accountDao.readAccount())将被执行多少次。

lazy 委托将在第一次访问时执行一次 lambda 并记住结果。如果再次调用,则返回缓存的结果。

另一方面,定义getter (get()) 每次都会执行函数体,每次都返回一个新的结果。

例如,假设我们有一个名为 Foo 的 class,同时具有 getter 和惰性值:

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

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

然后使用它:

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

对我来说,这会打印:

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

而且我们可以看到getterreturns每次都是新计算的值,和惰性版本returns相同的缓存值。

除了托德的回答:

是的,LiveData 对象也有差异。每次调用 accountDao.readAccount() 都会产生一个 不同的 LiveData 对象。这确实很重要,尽管所有返回的 LiveData 都会在 Account 实体的每次更改时得到更新。让我解释一下这些例子:

  • by lazy

正如 Todd 提到的,lazy 委托中的块将被执行一次,在第一次访问 account 属性 时,结果将被缓存并返回在每次下一次访问时。因此,在这种情况下,将创建一个 LiveData<Account> 对象。 Kotlin 生成的字节码实现这一点相当于 Java:

public class Activity {

    private Lazy account$delegate

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

通过创建自定义 account 属性 的 getter 并在内部调用 accountDao.readAccount(),您将在每次访问时得到不同的 LiveData<Account> 对象account 属性。再一次,在 Java 中为这种情况在 Kotlin 中生成的字节码或多或少是这样的:

public class Activity {

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

所以你可以看到,使用惰性 属性 会为此 属性 生成一个支持字段,而使用自定义 getter 会为 [=13 创建一个包装器方法=]打电话。

使用哪种方法取决于您的需要。我会说,如果你只需要获得一次 LiveData,你应该使用 get(),因为在这种情况下不需要支持字段。但是,如果您要在代码中的多个位置访问 LiveData,也许更好的方法是使用 by lazy 并仅创建一次。