根据官方文档翻译:
lazy { ... }只能被用在被
val
修饰的变量上,而lateinit
只能被用var
修饰的变量上,因为被lateinit
修饰的字段无法被编译为一个final
字段、因此无法保证它的不可变性。被
lateinit
修饰的变量可以在对象(代码)的任何地方进行初始化,而且同一个类的不同对象可以对这个变量进行多次的初始化(赋值)。但是,对于by lazy { ... }
修饰的变量,只拥有唯一一个声明在{}
中的初始化构造器,如果你想要修改它,你只能通过在子类中覆写的方式来修改它的值。所以,如果你想要你的属性在其他地方以不是你事先定义好的值初始化的话,使用lateinit
by lazy { ... }
的初始化默认是线程安全的,并且能保证by lazy { ... }
代码块中的代码最多被调用一次。而lateinit var
默认是不保证线程安全的,它的情况完全取决于使用者的代码。Lazy
实例是有值的,这个值可以被存储、传递和使用。但是,被lateinit var
修饰的变量不存储任何多余的运行时状态,只有值还未被初始化的null
值。如果你持有一个
Lazy
实例的引用,你可以使用它的isInitialized()
方法来判断它是否已经被初始化。从Kotlin1.2开始,你也可以使用方法引用的方式来获取这个值。by lazy { ... }
中传递的lambda表达式可能会捕获它的闭包中使用的上下文的引用,引用会一直被持有直到变量被初始化。因此这样可能会导致内存泄漏,所以仔细考虑你在lambda表达式中使用的值是否合理。
总结:
(1)作用的对象不一样
lateinit只能作用于被var关键字修饰的变量(并且这个值);而by lazy{ }只能作用于被val 关键字修饰的变量。
(2)初始化时间
lateinit修饰的变量需要由开发者自己决定初始化的时间、位置,并且同一个类的不同对象可以对这个变量进行多次的初始化;而by lazy{ }修饰的变量在声明的时候就需要在lazy{ }代码块中制定延迟初始化的行为,并且在该属性被第一次使用的时候自动完成初始化。
(3)线程安全方面
lateinit默认是不保证线程安全的,它的情况完全取决于使用者的代码;而by lazy{ }的初始化默认线程安全,并且保证代码块中的代码最多被调用一次。
(4)内存泄露问题
by lazy{ }中的lambda表达式可能会含有上下文的引用,引用会一直被持有直到变量被初始化。因此这样可能会导致内存泄漏。
(5)值是否为null
lateinit如果未进行初始化的赋值操作,为null值;而lazy实例是有值的,只要使用就一定非空(一经使用就立即完成初始化操作)。