Kotlin中Lateinit 和 Lazy 的区别
latinit
我们可能不想在定义它们时初始化我们的值,相反我们可能想在以后的任何时间初始化并在我们的应用程序中使用它们。但是在使用我们的值之前,一定要记住,我们的值必须先初始化,然后才能使用。让我们举个例子更好地理解!
class MainActivity : AppCompatActivity() {
//Here is the value we don't want to initialize at declaration time,
// so we used the lateinit keyword.
private lateinit var myUser:User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//We can initialize our value at any time in our application.
myUser = User("Hüseyin","Özkoç")
println("MY NAME IS : " + myUser.name)
}
}
data class User(var name: String, var surname: String)
正如我们在上面的例子中看到的,我们在识别它的时候并没有初始化我们的值。相反,我们在应用程序的 OnCreate() 函数中对其进行初始化,并使用 print() 方法将我们的值打印到 LogCat。如果我们尝试在不初始化的情况下将我们的值打印到 LogCat 会怎样?让我们试试看!
//We can initialize our value at any time in our application.
//myUser = User("Hüseyin","Özkoç")
println("MY NAME IS : " + myUser.name)
如上所示,如果我们尝试在未初始化的情况下访问我们的值,我们将遇到UninitializedPropertyAccessException
错误。我们也可以使用Kotlin 提供的isInitialized()
方法来避免这个错误。
class MainActivity : AppCompatActivity() {
//Here is the value we don't want to initialize at declaration time,
//So we used the lateinit keyword.
private lateinit var myUser: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/**
We can check if our value is initialize using isInitialized.
*/
if (this::myUser.isInitialized) {
println("MY NAME IS : " + myUser.name)
} else {
myUser = User("Hüseyin", "Özkoç")
println("MY NAME IS : " + myUser.name)
}
}
}
data class User(var name: String, var surname: String)
使用by lazy
延迟初始化是我们在软件世界中经常遇到的一种设计模式。使用惰性初始化,我们只能在第一次访问对象时创建对象,否则我们不需要初始化它们。它确保创建成本高昂的对象仅在要使用它们的地方初始化,而不是在应用程序启动时初始化。
当我们想在一个类中创建一个对象时,Kotlin 中的 Lazy 非常有用,但是该对象的创建是昂贵的,并且可能导致创建依赖于该昂贵对象的对象的延迟。因此,我们需要明白,对象只有在第一次访问时才会被初始化,否则不会被初始化。
让我们举个例子更好地理解!
class MainActivity : AppCompatActivity() {
//Here is the value we don't want to initialize at declaration time,
//So we used the lateinit keyword.
private lateinit var myButton: Button
/**
* Suppose we have this expensive object
* that will initialize only when we need it.
*/
private val myUser: User by lazy {
print("Lazy initialization")
User("Hüseyin", "Özkoç")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myButton = findViewById(R.id.myButton)
myButton.setOnClickListener {
/**
* We want this object to be created only once
* when the button is clicked, not inside the onCreate() method.
*
* Therefore, the moment we use our myUser value,
* our value will be initialized once and
* we will use that initialized value for all other clicks.
*/
println(myUser.name)
}
}
}
data class User(var name: String, var surname: String)
正如我们在上面的例子中看到的,当我们试图访问我们的对象时,我们的值只被初始化一次,然后我们在每次点击时使用那个初始化的对象。
Lateinit 和 Lazy 有什么区别?
-
lateinit
仅限于可变(var)变量属性,而lazy
修饰符仅用于只读(val)。
-
- 虽然标有
lateinit
的值可以在运行时多次赋值,但用lazy
初始化的值只能在第一次使用时赋值一次。
- 虽然标有
-
- 虽然不可能将原始数据类型定义为
lateinit
值,但“惰性”值可以是原始数据类型或非原始数据类型。(如整数)
- 虽然不可能将原始数据类型定义为
-
- 虽然无法确保
lateinit
值的线程安全,但我们可以为lazy
值选择同步选项之一,例如SYNCHRONIZED、PUBLICATION、NONE
。(这就是我们在使用Singleton
设计模式时使用 lazy 的原因。)
- 虽然无法确保
-
lateinit
值不能定义为nullable
,但是“lazy”可以定义为nullable
和non-nullable
。
-
lateinit
值不能有自定义的getter
,而“lazy”值包含在第一次调用该值时运行的代码块。
-
- 尝试在初始化之前访问
lateinit
属性将导致错误,指出该值未初始化。另一方面,“惰性”值在初始化之前无法访问。重要的是要注意“惰性”属性可以为null
,但该值仍会在第一次访问时被初始化。
- 尝试在初始化之前访问
参考
https://kotlinlang.org/
https://amitshekhar.me/blog/lateinit-vs-lazy-in-kotlin
https://agrawalsuneet.github.io/blogs/lateinit-vs-lazy-property-in-kotlin/