原文内容来自 Kotlin - Property initialization using “by lazy” vs. “lateinit”

lazy { ... } delegate can only be used for val properties, whereas lateinit can only be applied to vars, because it can't be compiled to a final field, thus no immutability can be guaranteed;

  • lazy { ... }只能被用在被val修饰的变量上,而lateinit只能被用var修饰的变量上,因为被lateinit修饰的字段无法被编译为一个final字段、因此无法保证它的不可变性。

lateinit var has a backing field which stores the value, and by lazy { ... } creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, use lateinit

  • lateinit修饰的变量有一个幕后字段用来存储它的值,而by lazy { ... }创建了一个包含by lazy { ... }中代码返回值的实例对象,实例对象持有这个值并生成一个可以在实例对象中调用的这个值的getter。所以如果你需要在代码中使用幕后字段的话,使用lateinit

In addition to vals, lateinit cannot be used for nullable properties and Java primitive types (this is because of null used for uninitialized value)

  • 除了被val修饰的变量外,lateinit也不能被用来修饰可空的属性和java的基本类型(因为对于可空类型,会有默认值null

lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy { ... }, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit

  • lateinit修饰的变量可以在对象(代码)的任何地方进行初始化,而且同一个类的不同对象可以对这个变量进行多次的初始化(赋值)。但是,对于by lazy { ... }修饰的变量,只拥有唯一一个声明在{}中的初始化构造器,如果你想要修改它,你只能通过在子类中覆写的方式来修改它的值。所以,如果你想要你的属性在其他地方以不是你事先定义好的值初始化的话,使用lateinit

Initialization by lazy { ... } is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using another lazy overload). In case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments.

  • by lazy { ... }的初始化默认是线程安全的,并且能保证by lazy { ... }代码块中的代码最多被调用一次。而lateinit var默认是不保证线程安全的,它的情况完全取决于使用者的代码。

A Lazy instance can be saved, passed around and even used for multiple properties. On contrary, lateinit vars do not store any additional runtime state (only null in the field for uninitialized value).

  • Lazy实例是有值的,这个值可以被存储、传递和使用。但是,被lateinit var修饰的变量不存储任何多余的运行时状态,只有值还未被初始化的null值。

If you hold a reference to an instance of Lazy, isInitialized() allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can use property::isInitialized since Kotlin 1.2.

  • 如果你持有一个Lazy实例的引用,你可以使用它的isInitialized()方法来判断它是否已经被初始化。从Kotlin1.2开始,你也可以使用方法引用的方式来获取这个值。

A lambda passed to by lazy { ... } may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.

  • by lazy { ... }中传递的lambda表达式可能会捕获它的闭包中使用的上下文的引用,引用会一直被持有直到变量被初始化。因此这样可能会导致内存泄漏,所以仔细考虑你在lambda表达式中使用的值是否合理