Kotlin-延迟初始化
2020-12-30 本文已影响0人
有腹肌的豌豆Z
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的作用也比较简单,就是让编译期在检查时不要因为属性变量未被初始化而报错。假的延迟,后面忘记了 那就 GG了
- Kotlin相信当开发者显式使用lateinit var关键字的时候,他一定也会在后面某个合理的时机将该属性对象初始化的.
lateinit在Android中使用
private lateinit var s: String
private val ss:String by lazy { "132" }
fun main() {
print("懒加载 ss = $ss")
try {
print("没有初始化 是s = $s \n") // 必须要初始化之后才能使用
}catch (e:Exception){
e.printStackTrace()
}
s = "123456"
print("初始化之后的 s = $s")
}
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)
}
private val sss:String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { "最后一个函数 可以放在外面 " }
- 模式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()
}
}
}