Kotlin - Property initialization using “by lazy” vs. “lateinit”
在Kotlin中,如果不想在构造函数内或在类体顶部初始化类属性,则基本上有这两个选项(从语言引用中):
lazy() is a function that takes a lambda and returns an instance of Lazy 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
1
2
3
4
5 public class Hello {
val myLazyString: String by lazy {"Hello" }
}
所以第一次呼叫和随后的呼叫,无论在哪里,到mylazystring都会返回"你好"
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:
1
2
3
4
5
6
7
8 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.
那么,如何在这两个选项之间做出正确的选择,因为两者都能解决同样的问题?
以下是
lazy { ... } 委托只能用于val 属性,而lateinit 只能应用于var 属性,因为它不能编译为final 字段,因此不能保证不可变性;lateinit var 有一个存储值的支持字段,by lazy { ... } 创建了一个委托对象,在该对象中,一旦计算出值,就会将对委托实例的引用存储在类对象中,并为与委托实例一起使用的属性生成getter。因此,如果需要类中存在的支持字段,请使用lateinit ;除了EDOCX1·3的S,EDOCX1·4)不能用于可空属性和Java基元类型(这是因为EDCOX1引用12用于未初始化值);
lateinit var 可以从任何可以看到对象的地方初始化,例如从框架代码内部初始化,并且单个类的不同对象可能有多个初始化场景。by lazy { ... } 反过来定义了该属性的唯一初始值设定项,只有通过重写子类中的属性才能更改该初始值设定项。如果您希望以一种可能事先未知的方式从外部初始化属性,请使用lateinit 。默认情况下,初始化
by lazy { ... } 是线程安全的,并保证最多调用一次初始值设定项(但这可以通过使用另一个lazy 重载来更改)。在lateinit var 的情况下,由用户的代码来正确初始化多线程环境中的属性。可以保存、传递甚至用于多个属性的
lazy 实例。相反,lateinit var 不存储任何额外的运行时状态(字段中只有null 用于未初始化的值)。如果您持有对
lazy 实例的引用,那么isInitialized() 允许您检查该实例是否已初始化(并且您可以通过委托属性的反射来获取该实例)。要检查lateinit属性是否已初始化,可以使用property::isInitialized ,因为kotlin 1.2。传递给
by lazy { ... } 的lambda可以捕获来自上下文的引用,在上下文中使用它来结束它。然后,它将存储引用,并仅在属性初始化后释放它们。这可能导致对象层次结构,如Android活动,不会被释放太长时间(或者,如果属性保持可访问性并且从未被访问过),因此您应该注意在初始值设定项lambda中使用什么。
另外,在该问题中没有提到另一种方式:EDCOX1(26),它适合于延迟非空属性的初始化,包括Java原始类型的初始化。
除了EDOCX1·27的好答案,以下是我在实践中如何选择:
EDCOX1 4是外部初始化:当你需要外部的东西来初始化你的价值,通过调用一个方法。
例如通过呼叫:
1 2 3 4 5 | private lateinit var value: MyClass fun init(externalProperties: Any) { value = somethingThatDependsOn(externalProperties) } |
而
非常简短的回答
lateInit:它最近初始化了非空属性
与延迟初始化不同,lateInit允许编译器识别非空属性的值没有存储在要正常编译的constructor阶段。
延迟初始化
当实现在Kotlin中执行惰性初始化的只读(val)属性时,by Lazy可能非常有用。
通过懒惰{…}在首次使用已定义属性的位置执行其初始值设定项,而不是其声明。
除了所有伟大的答案外,还有一个称为懒惰加载的概念:
Lazy loading is a design pattern commonly used in computer programming to defer initialization of an object until the point at which it is needed.
正确使用它,可以减少应用程序的加载时间。Kotlin实现它的方法是通过
但是,当您确定某个变量不会为空或为空时,就会使用lateinit,并且在使用它之前会对其进行初始化,例如在Android的
信贷转到@amit shekhar
拉蒂尼特
Lateinit是延迟初始化。
通常,声明为具有非空类型的属性必须在构造函数中初始化。然而,这通常是不方便的。例如,可以通过依赖注入或单元测试的设置方法初始化属性。在这种情况下,不能在构造函数中提供非空初始值设定项,但在引用类主体内的属性时,仍要避免进行空检查。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 | public class Test { lateinit var mock: Mock @SetUp fun setup() { mock = Mock() } @Test fun test() { mock.do() } } |
懒惰的
懒惰是懒惰的初始化。
例子:
1 2 3 | public class Example{ val name: String by lazy {"Amit Shekhar" } } |
1 2 3 4 5 6 7 8 9 10 11 | public class Late { lateinit var mercedes: Mercedes @SetUp fun setup() { mercedes = Mercedes() } @Test fun testing() { mercedes.do() } } |
1 2 3 4 | public class Lazy { val name: String by lazy {"Mercedes-Benz" } } |
如果您使用的是Spring容器,并且想要初始化不可为空的bean字段,那么
1 2 | @Autowired lateinit var myBean: MyBean |