gpt4 book ai didi

properties - 使用 "by lazy"与 "lateinit"进行属性初始化

转载 作者:IT老高 更新时间:2023-10-28 13:25:53 28 4
gpt4 key购买 nike

在 Kotlin 中,如果您不想在构造函数内或类体顶部初始化类属性,则基本上有以下两种选择(来自语言引用):

  1. Lazy Initialization

lazy() is a function that takes a lambda and returns an instance of Lazy<T> which can serve as a delegate for implementing a lazy property: the first call to get() executes the lambda passed to lazy() and remembers the result, subsequent calls to get() simply return the remembered result.

Example

public class Hello {

val myLazyString: String by lazy { "Hello" }

}

因此,无论在何处,第一次调用和随后的调用都将发送到 myLazyString将返回 Hello

  1. Late Initialization

Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.

To handle this case, you can mark the property with the lateinit modifier:

public class MyTest {

lateinit var subject: TestSubject

@SetUp fun setup() { subject = TestSubject() }

@Test fun test() { subject.method() }
}

The modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.

那么,既然这两个选项都可以解决同一个问题,那么如何正确选择呢?

最佳答案

以下是 lateinit varbylazy { ... } 委托(delegate)属性的显着区别:

  • lazy { ... } 委托(delegate)只能用于 val 属性,而 lateinit 只能应用于 vars,因为不能编译成final字段,不能保证不变性;

  • lateinit var 有一个存储值的支持字段,并且 bylazy { ... } 创建一个委托(delegate)对象,其中值存储一次计算后,将对委托(delegate)实例的引用存储在类对象中,并为与委托(delegate)实例一起工作的属性生成 getter。因此,如果您需要类中存在的支持字段,请使用 lateinit;

  • 除了 val 之外,lateinit 不能用于可空属性或 Java 原始类型(这是因为 null用于未初始化的值);

  • lateinit var 可以从任何可以看到对象的地方初始化,例如从框架代码内部,单个类的不同对象可以有多个初始化场景。 bylazy { ... } 反过来定义了属性的唯一初始化程序,只能通过覆盖子类中的属性来更改它。如果您希望您的属性以一种事先可能未知的方式从外部初始化,请使用 lateinit

  • 初始化 bylazy { ... } 默认是线程安全的,并保证初始化器最多被调用一次(但这可以通过使用 another lazy overload 来改变)。在 lateinit var 的情况下,在多线程环境中正确初始化属性取决于用户的代码。

  • Lazy 实例可以被保存、传递,甚至可以用于多个属性。相反,lateinit vars 不存储任何额外的运行时状态(只有 null 字段中的未初始化值)。

  • 如果您持有对 Lazy 实例的引用,isInitialized()允许你检查它是否已经被初始化(你可以obtain such instance with reflection从一个委托(delegate)属性)。要检查一个lateinit属性是否已经初始化,可以use property::isInitialized since Kotlin 1.2 .

  • 通过lazy { ... } 传递给的lambda 可能会从使用它的上下文中捕获引用到它的closure 中。 .. 然后它将存储引用并仅在属性初始化后才释放它们。这可能会导致对象层次结构(例如 Android 事件)不会被释放太久(或者永远不会被释放,如果该属性仍然可以访问并且永远不会被访问),因此您应该小心在初始化程序 lambda 中使用的内容。




另外,问题中没有提到另一种方法:Delegates.notNull() ,适用于非空属性的延迟初始化,包括Java基本类型。








关于properties - 使用 "by lazy"与 "lateinit"进行属性初始化,我们在Stack Overflow上找到一个类似的问题:

https://stackoverflow.com/questions/36623177/




28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com