Lazy vs Late init 🦖

Ta Theerasan Tonthongkam
ta tonthongkam
Published in
3 min readJul 24, 2019

Kotlin สามารถประกาศตัวแปรได้หลายวิธี และแต่ละวิธีก็มีการทำงาน และลักษณะการทำงานที่ต่างกันไป — เมื่อเราเขียน โปรแกรมมากๆ เข้า เราอาจจะเริ่มคำนึงถึง Flow การทำงาน เช่น สร้างตัวแปรเมื่อจำเป็น หรือสร้างตัวแปรเมื่อพร้อมจะสร้าง แน่นอนว่า Kotlin ได้เตรียม Modifier หรือ function ที่รองรับการทำงานดังกล่าวมาให้นั้นคือ Lazy และ Late init นั้นเอง

Lazy

เป็น function หนึ่งใน Kotlin Standard Delegates — โดย Function นี้เป็น lamda return Lazy<T> ลักษณะเด่นคือเมื่อเรียก get() function จะถูก executes เพียงครั้งแรกครั้งเดียวเท่านั้น จากนั้นจะ Return ค่ากลับไป — เมื่อมีการมีการเรียก get() จะไม่มีการ executes เพิ่ม แต่จะคืนค่าเดิมกลับไป

เช่น

//ตัวอย่างที่ 1val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
//output
computed!
Hello
Hello

จาก ตัวอย่างที่ 1 จะเห็นว่า lazyValue.get() executes แค่ครั้งเดียว แล้ว return ค่าเดิมกลับเสมอ

//ตัวอย่างที่ 2var a = 1val lazyValue: Int by lazy {
println("computed!")
a + 1
}
fun main() {
println("lazy value $lazyValue : a value $a")
a = 2
println("lazy value $lazyValue : a value $a")
}
//output
computed!
lazy value 2 : a value 1
lazy value 2 : a value 2

ตัวอย่างที่ 2 จะเห็นว่า a มีการเปลี่ยนค่า แต่เมื่อมีการเรียก lazyValue.get() แล้ว ไม่ว่า a จะเปลี่ยนค่าเป็นอะไรก็ไม่ส่งผลกับ lazyValue.get()

โดยทั่วไป ค่าของ lazy จะเป็น synchronized — ค่าจะถูกทำงานใน thread ใด thread หนึ่ง แต่ทุก thread จะเห็นค่าเดียวกัน; แต่ถ้าการ synchronization ของค่าเริ่มต้นไม่ได้ถูกกำหนดแต่แรก multiple threads สามารถ execute lazy ได้พร้อมกัน โดยผ่านพารามิเตอร์ LazyThreadSafetyMode.PUBLICATION ไปยัง lazy() function.

ในทางกลับกัน หากมั่นใจว่า initialization จะเกิดใน thread เดียวเท่านั้น สามารถใช้โหมด LazyThreadSafetyMode.NONE แทนได้ แต่ไม่การันตี thread-safety หรือ overhead

Late-Initialized Properties and Variables

โดยปกติแล้ว properties แบบ Not null ใน Kotlin จะต้องถูกกำหนดที่ Constuctor เท่านั้น แต่บางครั้งการจะกำหนดค่าของ properties ไม่สามารถทำได้ทันที ซึ่งอาจจะต้องรอการทำงานของ Function ใด Function หนึ่งก่อน — การประกาศ properties แบบ Not null ช่วยให้ไม่ต้องเขียน null check ทุกครั้งที่เรียกใช้ และบางครั้ง อาจเป็นข้อกำหนดของ Database ก็ได้

ในลักษณะนี้สามารถใช่ Modifier lateinit มาจัดการได้

public class Employee{
lateinit var name: String
fun apply() {
name = "Ta"
}
fun callEmplpoyee() {
println("Hello, $name")
}
}

modifier lateinit ต้องใช้คู่กับ var เท่านั้น และค่าของ properties ต้องถูกกำหนดใน class ที่ไม่ใช่ contructor — และ properties ต้องไม่มี custom getter/setter ด้วย

ตั้งแต่ Kotlin 1.2 เป็นต้นไป Type ของ lateinit จะต้องไม่เป็น nullable และไม่เป็น primitive

Error ahead 🚨

การเข้าถึงค่าของ lateinit ก่อนกับหนดค่าจะทำให้เกิด exception ดังนี้

แต่เราสามารถเช็คได้จาก isInitialized เช่นโค้ดข้างล่าง

class MyClass {
lateinit var subject: String

fun main() {
println("isInitialized ${this::subject.isInitialized}")
subject = "Test"
println("isInitialized ${this::subject.isInitialized}")
}
}
output

สรุปความต่างระหว่าง Lazy และ Late init

ทั้ง lazy และ Late init เป็นวิธีการประกาศตัวแปรที่มีความสามารถต่างๆ และใช้ในสถานการณ์ที่แตกต่างกัน สามารถสรุปได้ตามตาราง ดังนี้

Thank you

— end of the story —

--

--