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

延迟初始化

lazy()是一个接受lambda并返回lazy <T>实例的函数,它可以作为实现lazy属性的委托:第一次调用get()执行传递给lazy()的lambda并记住结果,后续调用get()只返回记住的结果。 例子 公共类Hello { val myLazyString:通过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和by lazy{…}委托的属性:

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; 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; In addition to vals, lateinit cannot be used for nullable properties or Java primitive types (this is because of null used for uninitialized value); 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. 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 the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments. 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). 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. 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.

此外,问题中还没有提到另一种方法:delegates.notull(),它适用于延迟初始化非空属性,包括Java原语类型的属性。

其他回答

下面是lateinit var和by lazy{…}委托的属性:

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; 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; In addition to vals, lateinit cannot be used for nullable properties or Java primitive types (this is because of null used for uninitialized value); 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. 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 the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments. 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). 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. 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.

此外,问题中还没有提到另一种方法:delegates.notull(),它适用于延迟初始化非空属性,包括Java原语类型的属性。

除了热键的好答案之外,下面是我在实践中如何在这两个选项中进行选择:

Lateinit用于外部初始化:当你需要通过调用一个方法来初始化你的值时。

例如,通过呼叫:

private lateinit var value: MyClass

fun init(externalProperties: Any) {
   value = somethingThatDependsOn(externalProperties)
}

lazy是指它只使用对象内部的依赖关系。

如果您正在使用Spring容器,并且希望初始化不可空的bean字段,lateinit更适合。

    @Autowired
    lateinit var myBean: MyBean

Everything is correct above, but one of facts simple explanation LAZY----There are cases when you want to delay the creation of an instance of your object until its first usage. This technique is known as lazy initialization or lazy instantiation. The main purpose of lazy initialization is to boost performance and reduce your memory footprint. If instantiating an instance of your type carries a large computational cost and the program might end up not actually using it, you would want to delay or even avoid wasting CPU cycles.

非常简短和简洁的回答

lateinit:它最近初始化非空属性

与延迟初始化不同,lateinit允许编译器识别非空属性的值没有存储在构造函数阶段以进行正常编译。

延迟初始化

在Kotlin中实现执行惰性初始化的只读(val)属性时,by lazy可能非常有用。

懒人{…}在第一次使用已定义属性的地方执行其初始化式,而不是其声明。