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 , 歡迎關注

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