Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

lazy و lateinit

جزء من قسم البرمجة كائنية التوجه في رحلة Kotlin على Coddy. الدرس 10 من 57.

تؤجل by lazy { ... } حساب val حتى تتم قراءته للمرة الأولى، ثم تتذكر النتيجة. وهي مناسبة للقيم التي يكون حسابها مكلفًا أو التي لا تكون مطلوبة دائمًا:

class Report(val rows: List<Int>) {
    val summary: String by lazy {
        println("computing summary")
        "total ${rows.sum()}"
    }
}

داخل main:

val r = Report(listOf(3, 4))
println("created")
println(r.summary)
println(r.summary)

الناتج:

created
computing summary
total 7
total 7

تُنفَّذ الكتلة مرة واحدة: استخدمت القراءة الثانية أعلاه النتيجة المخزَّنة. بخلاف getter مخصّص، يُنفَّذ عند كل قراءة، فإن lazy يحسب مرة واحدة ويخزّن النتيجة مؤقتًا:

class Demo {
    var reads = 0
    val viaGetter: Int
        get() { reads++; return 42 }
    val viaLazy: Int by lazy { reads++; 42 }
}

داخل main:

val d = Demo()
repeat(3) { d.viaGetter; d.viaLazy }
println(d.reads)

الناتج:

4

يُصرّح lateinit var عن خاصية غير قابلة للقيمة الفارغة يتم إسناد قيمة إليها لاحقًا، بعد الإنشاء، مثلًا بواسطة طريقة إعداد. تؤدي قراءتها قبل إسناد قيمة إليها إلى طرح استثناء، وتتحقق ::name.isInitialized من ذلك:

class Screen {
    lateinit var title: String

    fun load(t: String) { title = t }
    fun ready() = ::title.isInitialized
}

داخل main:

val s = Screen()
println(s.ready())
s.load("Home")
println(s.ready())
println(s.title)

الناتج:

false
true
Home

فضّل التهيئة العادية عندما تكون القيمة معروفة عند الإنشاء. استخدم lazy مع قيمة val مكلفة قد لا تكون مطلوبة، واستخدم lateinit فقط مع var التي تصل قيمتها فعلًا لاحقًا؛ لا تعمل lateinit مع Int أو Boolean أو الأنواع البدائية الأخرى.

val config by lazy { loadConfig() }     // يُحسب عند أول استخدام، مرة واحدة
lateinit var user: User                 // يُعيَّن بعد الإنشاء
// lateinit var count: Int              // خطأ: غير مسموح به على الأنواع البدائية
challenge icon

التحدي

سهل

أكمل الصنف Document(val text: String). أضف wordCount كخاصية lazy تحسب عدد الكلمات (المفصولة بمسافات مفردة) وتسجل كل عملية حساب بإضافة 1 إلى computations، وأضف lateinit var author: String مع دالة credit() تُرجع by Ada أو by unknown عندما لا يكون المؤلف قد عُيّن.

يقرأ الكود المُقدَّم النص، ويقرأ wordCount ثلاث مرات، ويعيّن المؤلف عند وجود سطر ثانٍ، ويطبع words 5, computed 1, by Ada.

ضع التعليمات البرمجية الخاصة بك في Document.kt. يحتوي Main.kt على كود الإدخال/الإخراج المُقدَّم ولا يمكن تعديله.

جرّب بنفسك

fun main() {
    // كود الإدخال/الإخراج المُزوَّد: اتركه كما هو
    val input = generateSequence(::readLine).toList()
    val doc = Document(input[0])
    val reads = List(3) { doc.wordCount }
    val words = reads.first()
    if (input.size > 1) doc.author = input[1]
    println("words $words, computed ${doc.computations}, ${doc.credit()}")
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: Playground لـ Kotlin