转载请标明出处:https://blog.csdn.net/zhaoyanjun6/article/details/93764289
本文出自【赵彦军的博客】
Koltin
中属性在声明的同时也要求要被初始化,否则会报错。例如以下代码:
private var name0: String //报错
private var name1: String = "xiaoming" //不报错
private var name2: String? = null //不报错
可是有的时候,我并不想声明一个类型可空的对象,而且我也没办法在对象一声明的时候就为它初始化,那么这时就需要用到Kotlin
提供的延迟初始化。Kotlin
中有两种延迟初始化的方式。一种是 lateinit var
,一种是by lazy
。
lateinit 延迟初始化
private lateinit var name: String
lateinit var
只能用来修饰类属性,不能用来修饰局部变量,并且只能用来修饰对象,不能用来修饰基本类型(因为基本类型的属性在类加载后的准备阶段都会被初始化为默认值)。lateinit var
的作用也比较简单,就是让编译期在检查时不要因为属性变量未被初始化而报错。Kotlin
相信当开发者显式使用lateinit var
关键字的时候,他一定也会在后面某个合理的时机将该属性对象初始化的.
lateinit
在Android
中使用
class MainActivity : AppCompatActivity() {
private lateinit var bt: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bt = findViewById(R.id.bt)
bt.setOnClickListener {
Toast.makeText(baseContext, "click", Toast.LENGTH_SHORT).show()
}
}
}
lazy 延迟初始化
by lazy
本身是一种属性委托。属性委托的关键字是by
。by lazy
的写法如下:
//用于属性延迟初始化
val name: Int by lazy { 1 }
//用于局部变量延迟初始化
public fun foo() {
val bar by lazy { "hello" }
println(bar)
}
by lazy
要求属性声明为val
,即不可变变量,在java
中相当于被final
修饰。这意味着该变量一旦初始化后就不允许再被修改值了(基本类型是值不能被修改,对象类型是引用不能被修改)。{}内的操作就是返回唯一一次初始化的结果。by lazy
可以使用于类属性或者局部变量。
在 Android 中使用
class MainActivity : AppCompatActivity() {
private val bt by lazy {
findViewById<Button>(R.id.bt)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bt.setOnClickListener {
Toast.makeText(baseContext, "click", Toast.LENGTH_SHORT).show()
}
}
}
lazy 延迟模式
在使用 lazy
延迟初始化的时候,Kotlin提供了3中模式,源码如下:
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
-
模式1:
LazyThreadSafetyMode.SYNCHRONIZED
线程安全模式,Initializer函数只能被调用一次,返回的对象只有一个 -
模式2:
LazyThreadSafetyMode.PUBLICATION
在对未初始化的[Lazy]实例值进行并发访问时,可以多次调用Initializer函数,但只有第一个返回值将用作[Lazy]实例的值。 -
模式3:
LazyThreadSafetyMode.NONE
没有锁用于同步对[Lazy]实例值的访问; 如果从多个线程访问实例,可能会有多个实例。除非保证[Lazy]实例永远不会从多个线程初始化,否则不应使用此模式。
当我们模式都不用的情况下,默认使用 LazyThreadSafetyMode.SYNCHRONIZED
线程安全模式。源码如下:
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
几个例子,使用延迟模式创建一个单例
class Manager {
init {
Log.e("zhaoyanjun:inin", "初始化")
}
companion object {
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Manager()
}
}
}
总结
那么,再总结一下,lateinit var
和by lazy
哪个更好用?
首先两者的应用场景是略有不同的。然后,虽然两者都可以推迟属性初始化的时间,但是lateinit var
只是让编译期忽略对属性未初始化的检查,后续在哪里以及何时初始化还需要开发者自己决定。
而by lazy
真正做到了声明的同时也指定了延迟初始化时的行为,在属性被第一次被使用的时候能自动初始化。但这些功能是要为此付出一丢丢代价的。
个人微信号:zhaoyanjun125 , 欢迎关注