Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

Kotlin: เริ่มต้น Property ด้วย by lazy vs lateinit

ภาพรวม

Kotlin มีกลไกสองแบบสำหรับการเริ่มต้นค่า Property แบบล่าช้า (Lazy Initialization): lateinit และ by lazy ทั้งคู่แก้ปัญหา "ต้องการประกาศตัวแปรแต่ยังไม่มีค่าในขณะสร้าง Object" แต่มีความแตกต่างสำคัญ

lateinit - เริ่มต้นภายหลังด้วยตนเอง

เงื่อนไขการใช้งาน

  • ต้องเป็น var (mutable)
  • ต้องเป็น Non-null type
  • ไม่รองรับ Primitive types (Int, Boolean, etc.)
  • ไม่มี Custom Getter/Setter

ตัวอย่าง

class Tutorial {
    lateinit var name: String

    fun checkLateInit() {
        // ตรวจสอบว่า initialize แล้วหรือยัง
        if (::name.isInitialized) {
            println("ค่า: $name")
        } else {
            println("ยังไม่ได้กำหนดค่า")
            name = "www.tutorialspoint.com"
            println("กำหนดค่าแล้ว: $name")
        }
    }
}

fun main() {
    val obj = Tutorial()
    obj.checkLateInit() // ยังไม่ได้กำหนดค่า -> กำหนดค่า -> แสดงผล
    obj.checkLateInit() // แสดงค่าที่กำหนดไว้แล้ว
}

คุณสมบัติ

  • คุณควบคุม เมื่อไหร่ และ อย่างไร ที่จะกำหนดค่า
  • สามารถกำหนดค่าใหม่ได้หลายครั้ง (เพราะเป็น var)
  • ถ้าเข้าถึงก่อน initialize → โยน UninitializedPropertyAccessException
  • ตรวจสอบได้ด้วย ::property.isInitialized

by lazy - เริ่มต้นอัตโนมัติครั้งแรกที่เข้าถึง

เงื่อนไขการใช้งาน

  • ต้องเป็น val (immutable reference)
  • Thread-safe โดยค่าเริ่มต้น (SYNCHRONIZED mode)
  • คำนวณค่าเพียงครั้งเดียว แล้ว Cache ไว้ใช้ต่อไป

ตัวอย่าง

class Demo {
    val myName: String by lazy {
        println("🔄 กำลังคำนวณค่า... (รันเพียงครั้งเดียว)")
        "www.tutorialspoint.com"
    }
}

fun main() {
    val obj = Demo()
    println("ครั้งที่ 1: ${obj.myName}") // พิมพ์ "กำลังคำนวณ..." แล้วค่า
    println("ครั้งที่ 2: ${obj.myName}") // ใช้ค่า Cache ทันที ไม่พิมพ์ข้อความ
    println("ครั้งที่ 3: ${obj.myName}") // ใช้ค่า Cache
}

โหมด Lazy (LazyThreadSafetyMode)

// Default: SYNCHRONIZED - Thread-safe, ใช้ Lock
val default: String by lazy { "default" }

// PUBLICATION - หลาย Thread อาจคำนวณซ้ำ แต่ใช้ค่าตัวแรกที่เสร็จ
val fast: String by lazy(LazyThreadSafetyMode.PUBLICATION) { "fast" }

// NONE - ไม่ Thread-safe, เร็วที่สุด (ใช้เมื่อแน่ใจว่า Single-thread)
val unsafe: String by lazy(LazyThreadSafetyMode.NONE) { "unsafe" }

ตารางเปรียบเทียบ

ลักษณะlateinit varval by lazy
MutabilityMutable (var)Immutable reference (val)
การกำหนดค่าManual (คุณตัดสินใจ)Automatic (ครั้งแรกที่เข้าถึง)
Primitive Typesไม่รองรับรองรับ (ผ่าน Wrapper/Inline Class)
NullabilityNon-null onlyNullable OK (val name: String? by lazy { ... })
Re-initializationได้ (assign ใหม่ได้)ไม่ได้ (คำนวณครั้งเดียวจบ)
Thread Safetyต้องจัดการเองBuilt-in (SYNCHRONIZED)
การตรวจสอบ Init::prop.isInitializedไม่จำเป็น (มั่นใจว่า Init แล้ว)

แนวทางเลือกใช้

  • lateinit: Dependency Injection, View Binding, Unit Test Setup, ค่าที่ตั้งค่าจากภายนอก
  • by lazy: Expensive Computation, Singleton Pattern, Cache, ค่าที่คำนวณจาก Property อื่น