Kotlin 惰性初始化 vs 延遲初始化 的解釋

https://proandroiddev.com/kotlin-lazy-vs-lateinit-explained-3574d5ec7d15

Kotlin Lazy vs Lateinit 解釋

探索初始化的方法

類的屬性是每個程式碼庫的基本部分。看看一些初始化它們的方法,以簡化和提高效率和程式碼的可讀性!

根據您的需求,您可能希望屬性是不可變的 val 或可變的 var 。在 Kotlin 中,我們有延遲(lazy)和延遲初始化(lateinit)屬性初始化器。

lazy()

lazy 只能用於 val 。正如其名所示,屬性是懶惰地初始化的,意味著它在首次使用時運行初始化代碼。

透過這個我們得到什麼?

我們確定值不會發生變化。

更好的資源管理。有時,您將不得不初始化多個屬性,其中一些可能需要花費大量時間。這是一個考慮使用懶惰初始化並延遲某些工作以改善啟動時間和記憶體分配的地方。

看一下這個範例:

類示例 {
val lazyExample by lazy { “範例” }

fun useExample() {
println(lazyExample)
}
}

fun main() {
val example = Example()
// 現在 `lazyExample` 屬性尚未初始化
example.useExample()
// 現在 `lazyExample` 有一個值並已初始化,因為它在 `useExample()` 函數中使用了
}

fun main() {
val example = Example()
// 外部讀取屬性也會初始化它
println(example.lazyExample)
}

就像在範例中一樣,大多數時候,您會希望您的懶惰使用一個 lambda 函數來初始化屬性是一種直觀的方式。

實際上,lazy 只是 Kotlin 代理的智慧使用。默認情況下,它使用初始化器的線程安全版本。

總之,下面的程式碼有一個在啟動時未初始化的內部變量,並且在每次 get() 時進行檢查:

// Kotlin 標準程式庫代碼
public actual fun lazy(initializer: () -> T): Lazy = SynchronizedLazyImpl(initializer)

private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this

override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress(“UNCHECKED_CAST”)
return _v1 as T
}

return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress(“UNCHECKED_CAST”) (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}

override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else “Lazy value not initialized yet.”
private fun writeReplace(): Any = InitializedLazyImpl(value)
}

lateinit

lateinit 只能用於 var 。您可能希望屬性隨著時間的推移而改變,並將其留在未初始化的狀態。

它的工作方式與 lazy 不同。在這裡,您並不確定屬性是否已初始化。使用 lateinit 就像告訴編譯器您知道自己在做什麼,而且該屬性將在第一次讀取屬性之前初始化。否則,它會導致程式崩潰。

透過這個我們得到什麼?

雖然 lazy 只能在 lambda 內初始化;lateinit 可以在程序的任何位置初始化,這對於瓶頸優化是一個很好的地方。

可變性是您在特定情況下想要的。

看一下它是如何運作的:

類示例 {
lateinit var lazyExample: String

fun initializeExample() {
lazyExample = “範例”
}

fun useExample() {
println(lazyExample)
}
}

// 程式崩潰範例
fun main() {
val example = Example()

example.useExample() // 程式崩潰: UninitializedPropertyAccessException
}

// 正常運作範例
fun main() {
val example = Example()

example.initializeExample() // 首先變數需要初始化
example.useExample() // 印出範例
}

實際上,您可以檢查變數是否已初始化並僅運行初始化程式碼:

類示例 {
lateinit var lazyExample: String

fun initializeExample() {
lazyExample = “範例”
}

fun useExample() {
// 真實應用中不要這樣做
if (::lazyExample.isInitialized) {
println(lazyExample)
} else {
initializeExample()
println(lazyExample)
}
}
}

然而,您絕對不應該在您的程式中使用它!isInitialized 只應純粹用於測試,因為它使用反射且非常難以閱讀。如果您需要在運行時檢查變數是否已初始化,請使用可為空版本:

類示例 {
var lazyExample: String? = null

fun initializeExample() {
lazyExample = “範例”
}

fun useExample() {
if (lazyExample != null) {
println(lazyExample)
} else {
initializeExample()
println(lazyExample)
}
}
}

基本類型

通常,無法在原始類型中使用 lateinit,因為 Kotlin 會創建一個儲存屬性的後備字段。由於這個原因,您無法使用原始類型。

相反,您應該使用 Delegates.notNull() 。其功能相同。唯一變的是簽名:

類示例 {
// 用於原始類型
var lazyExample by Delegates.notNull()

fun initializeExample() {
lazyExample = 1
}

fun useExample() {
println(lazyExample)
}
}

感謝您的閱讀!如果您學到了新知識,請關注我以獲得更多信息!

via ProAndroidDev – Medium

July 10, 2024 at 01:48AM

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *