在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 vs lazy

lateinit i)与可变变量[var]一起使用 lateinit var name: String //允许 lateinit val name: String //不允许

ii)只允许使用非空数据类型

    lateinit var name: String       //Allowed
    lateinit var name: String?      //Not Allowed

iii)这是对编译器的一个承诺,该值将在未来被初始化。

注意:如果你试图访问lateinit变量而没有初始化它,那么它会抛出UnInitializedPropertyAccessException异常。

懒惰的 i)延迟初始化是为了防止不必要的对象初始化。

ii)你的属性不会被初始化,除非你使用它。

iii)只初始化一次。下次使用它时,将从缓存中获取值。

iv)它是线程安全的(它在第一次使用的线程中初始化。其他线程使用缓存中存储的相同值)。

v)该属性只能为val。

vi)属性可以是任何类型(包括基本类型和空值,这是lateinit不允许的)。

其他回答

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

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

例如,通过呼叫:

private lateinit var value: MyClass

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

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

下面是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原语类型的属性。

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.

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

    @Autowired
    lateinit var myBean: MyBean

如果你使用一个不可改变的变量,那么最好使用by lazy{…在这种情况下,您可以确保它总是在需要时初始化,最多初始化1次。

如果你想要一个非空变量,可以改变它的值,使用lateinit var。在Android开发中,你可以在onCreate, onResume等事件中初始化它。注意,如果调用REST请求并访问这个变量,可能会导致一个异常UninitializedPropertyAccessException: lateinit属性yourVariable没有初始化,因为请求的执行速度比变量初始化快。