Lazy vs Late init 🦖
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}")
}
}
สรุปความต่างระหว่าง Lazy และ Late init
ทั้ง lazy และ Late init เป็นวิธีการประกาศตัวแปรที่มีความสามารถต่างๆ และใช้ในสถานการณ์ที่แตกต่างกัน สามารถสรุปได้ตามตาราง ดังนี้
Thank you
— end of the story —