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
并仅创建一次。
有房道如下,
@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
并仅创建一次。