在 Kotlin 中,如果您不想在构造函数内或类体顶部初始化类属性,则基本上有以下两种选择(来自语言引用):
lazy()
is a function that takes a lambda and returns an instance ofLazy<T>
which can serve as a delegate for implementing a lazy property: the first call toget()
executes the lambda passed tolazy()
and remembers the result, subsequent calls toget()
simply return the remembered result.Example
public class Hello { val myLazyString: String by lazy { "Hello" } }
因此,无论在何处,第一次调用和随后的调用都将发送到 myLazyString
将返回 Hello
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 var
和 bylazy { ... }
委托(delegate)属性的显着区别:
lazy { ... }
委托(delegate)只能用于 val
属性,而 lateinit
只能应用于 var
s,因为不能编译成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 var
s 不存储任何额外的运行时状态(只有 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/