Kotlin实战指南九:延迟初始化

转载请标明出处: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关键字的时候,他一定也会在后面某个合理的时机将该属性对象初始化的.

lateinitAndroid中使用

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本身是一种属性委托。属性委托的关键字是byby 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 varby lazy哪个更好用?
首先两者的应用场景是略有不同的。然后,虽然两者都可以推迟属性初始化的时间,但是lateinit var只是让编译期忽略对属性未初始化的检查,后续在哪里以及何时初始化还需要开发者自己决定。

by lazy真正做到了声明的同时也指定了延迟初始化时的行为,在属性被第一次被使用的时候能自动初始化。但这些功能是要为此付出一丢丢代价的。


个人微信号:zhaoyanjun125 , 欢迎关注

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章