在Kotlin中,如果您不想在构造函数内部或类体顶部启动类属性,那么基本上这两个选项(来自语言参考):
lazy()是一个函数,它接受一个lambda并返回一个Lazy实例,它可以作为实现一个惰性属性的委托:第一次调用get()执行传递给lazy()的lambda并记住结果,随后调用get()只是返回记住的结果 . 示例公共类Hello {
val myLazyString:lazy {“Hello”}的字符串
}
所以第一次调用和顺序调用,无论它在哪里,到 myLazyString 将返回 "Hello"
通常,必须在构造函数中初始化声明为具有非null类型的属性 . 但是,这通常不方便 . 例如,可以通过依赖注入或单元测试的设置方法初始化属性 . 在这种情况下,您无法在构造函数中提供非null初始值设定项,但在引用类体内的属性时仍希望避免空值检查 . 要处理这种情况,可以使用lateinit修饰符标记属性:public class MyTest {
lateinit var subject:TestSubject
@SetUp fun setup()
@Test fun test(){subject.method()}
}
修饰符只能用于在类体(不在主构造函数中)内声明的var属性,并且只能在属性没有自定义getter或setter时使用 . 属性的类型必须为非null,并且它不能是基本类型 .
那么,如何在这两个选项之间正确选择,因为它们都可以解决同样的问题?
5 回答
如果您正在使用Spring容器并且想要初始化非可空bean字段,则
lateinit
更适合 .非常简短的答案
lateinit: It initialize non-null properties lately
与延迟初始化不同, lateinit 允许编译器识别非null属性的值未存储在构造函数阶段中以进行正常编译 .
lazy Initialization
在实现在Kotlin中执行延迟初始化的 read-only (val)属性时, by lazy 可能非常有用 .
by lazy 执行其初始化程序,其中首先使用定义的属性,而不是其声明 .
Credit goes to @Amit Shekhar
lateinit
lateinit是迟到的初始化 .
通常,必须在构造函数中初始化声明为具有非null类型的属性 . 但是,这通常不方便 . 例如,可以通过依赖注入或单元测试的设置方法初始化属性 . 在这种情况下,您无法在构造函数中提供非null初始值设定项,但在引用类体内的属性时仍希望避免空值检查 .
Example:
lazy
懒惰是初始化的延迟 .
lazy()是一个函数,它接受一个lambda并返回一个lazy实例,它可以作为实现一个惰性属性的委托:第一次调用get()执行传递给lazy()的lambda并记住结果,后续调用get()只返回记住的结果 .
Example:
除了
hotkey
的好答案之外,以下是我在实践中如何选择:lateinit
用于外部初始化:当您需要外部资源来通过调用方法初始化您的值时 .例如致电:
虽然
lazy
是它只使用对象内部的依赖项 .以下是
lateinit var
和by lazy { ... }
委托属性之间的显着差异:lazy { ... }
委托只能用于val
属性,而lateinit
只能应用于var
,因为它无法编译为final
字段,因此无法保证不变性;lateinit var
有一个存储值的后备字段,by lazy { ... }
创建一个委托对象,其中值一旦计算就存储,将对委托实例的引用存储在类对象中,并为与委托实例一起使用的属性生成getter . 因此,如果您需要类中存在的支持字段,请使用lateinit
;除了
val
s之外,lateinit
不能用于可空属性和Java基元类型(这是因为null
用于未初始化的值);lateinit var
可以从看到对象的任何地方初始化,例如,从框架代码内部,可以为单个类的不同对象提供多个初始化方案 . 反过来,by lazy { ... }
定义了属性的唯一初始化程序,只能通过覆盖子类中的属性来更改 . 如果您希望以事先未知的方式从外部初始化您的属性,请使用lateinit
.初始化
by lazy { ... }
默认是线程安全的,并保证最多调用初始化程序一次(但这可以通过使用another lazy overload来改变) . 在lateinit var
的情况下,它是在多线程环境中正确初始化属性的's up to the user'代码 .Lazy
实例可以保存,传递甚至用于多个属性 . 相反,lateinit var
s不存储任何其他运行时状态(在未初始化值的字段中仅为null
) .如果您持有对
Lazy
实例的引用,isInitialized()允许您检查它是否已经初始化(并且您可以从委托属性中obtain such instance with reflection) . 要检查lateinit属性是否已初始化,您可以use property::isInitialized since Kotlin 1.2 .传递给
by lazy { ... }
的lambda可以捕获从它被用于closure的上下文中的引用 . 然后它将存储引用并仅在属性初始化后释放它们 . 这可能导致对象层次结构(例如Android活动)不会被释放太长时间(或者,如果属性仍然可访问且永远不会被访问),那么您应该注意在初始化程序lambda中使用的内容 .此外,问题中没有提到另一种方法:Delegates.notNull(),它适用于非空属性的延迟初始化,包括Java原始类型的延迟初始化 .