昨天发文因为是 markdown 写的复制过来直接看不到链接,没有配置原文,把链接放在这里了:【公众号文章】https://gitee.com/raybest4u/rays-io/blob/master/%20Jetpack%20Compose%20open%20source%20project.md
在Kotlin中,我们可以使用延迟初始化来推迟变量的初始化,以便在需要时进行初始化。Kotlin提供了两种延迟初始化的方式:lateinit和lazy。虽然两种方式都允许我们推迟变量的初始化,但它们之间有一些重要的区别。在本文中,我们将探讨这些区别并提供一些示例代码。
一、lateinit
lateinit是一种特殊的变量,它允许我们在使用变量之前不必立即初始化它。但是,我们必须在使用变量之前确保它已经被初始化,否则会抛出一个异常UninitializedPropertyAccessException。
下面是一个使用lateinit的示例:
class MyClass {
lateinit var str: String
fun init() {
str = "Hello, World!"
}
fun printStr() {
if (!::str.isInitialized) {
throw UninitializedPropertyAccessException("str has not been initialized yet.")
}
println(str)
}
}
fun main() {
val myClass = MyClass()
myClass.init()
myClass.printStr()
}
在这个示例中,我们定义了一个MyClass类,其中包含一个lateinit变量str。我们在init()方法中初始化了str变量,并在printStr()方法中使用了它。由于str变量是lateinit的,我们需要在使用它之前检查它是否已经被初始化。
二、lazy
lazy是一种懒加载变量,它允许我们在需要时进行初始化,并且只会初始化一次。此外,它还允许我们指定初始化的方式,例如通过lambda表达式或by lazy()函数。
下面是一个使用lazy的示例:
class MyClass {
val str: String by lazy {
println("Initializing str")
"Hello, World!"
}
fun printStr() {
println(str)
}
}
fun main() {
val myClass = MyClass()
myClass.printStr()
myClass.printStr()
}
在这个示例中,我们定义了一个MyClass类,其中包含一个lazy变量str。我们使用lambda表达式来初始化str变量,并在printStr()方法中使用了它。由于str变量是lazy的,它只会在第一次访问时进行初始化,并且在后续访问中直接返回已经初始化的值。
三、区别
虽然lateinit和lazy都允许我们延迟变量的初始化,但它们之间有一些重要的区别。下面是它们之间的主要区别:
-
• lateinit变量必须在使用之前进行初始化,否则会抛出异常。而lazy变量不需要在使用之前进行初始化,它只会在第一次访问时进行初始化。
-
• lateinit变量不支持原生类型,如Int或Double。而lazy变量可以是任何可空类型。
-
• lateinit变量只能在类中声明,而lazy变量可以在任何作用域中声明。
-
• lateinit变量只能使用var关键字声明,而lazy变量可以使用val或var关键字声明。
-
• 初始化时机:
-
• 当使用需要上下文的属性或在对象创建期间无法执行的大量初始化时,Lateinit特别有用。它通过将初始化推迟到更合适的时间(例如在特定方法或初始化块内)来提供灵活性。这可以通过避免不必要的计算或初始化直到实际需要它们来帮助提高性能。
-
• 另一方面,lazy初始化在处理初始化在计算上昂贵,但在对象的整个生命周期期间可能不需要的属性时是有益的。通过延迟初始化这样的属性,我们可以避免不必要的计算开销,并延迟初始化,直到属性被实际访问。这对于优化资源使用和提高应用程序的整体效率特别有利。
-
-
• 在线程安全性方面
-
• lazy属性提供了内置的同步机制,以确保初始化只发生一次,即使在多线程场景中也是如此。kotlin lazy 提供的委托默认是线程安全的,这意味着尝试同时访问属性的多个线程将等待初始化完成,然后共享相同的初始化值。
-
• lateinit属性不提供内置的线程安全机制。开发人员有责任确保在并发环境中访问lateinit属性之前正确初始化该属性。否则可能导致争用条件和不可预测的行为。
-
-
• 编译时错误与运行时错误:
-
• lateinit和lazy之间的一个关键区别是检测到与属性初始化相关的错误的时间。使用lateinit属性,如果属性在访问之前没有初始化,则 UninitializedPropertyAccessException 会在运行时引发。这可能是仅在运行时检测到的错误的来源,并且可能需要仔细调试以确定根本原因。
-
• 相反,lazy属性在编译时检查初始化。如果延迟属性未正确初始化或初始化逻辑引发异常,则将在编译过程中捕获该异常,从而允许开发人员在部署代码之前解决问题。这种早期错误检测有助于保持代码质量,并降低运行时崩溃的可能性。
-
总之,lateinit和lazy都是很有用的功能,它们可以帮助我们推迟变量的初始化,从而提高应用程序的性能和可维护性。我们可以根据需求选择合适的方式来初始化变量,并且可以根据需要对变量进行过滤或转换。