轉載請標明出處: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 , 歡迎關注