Menu
Coddy logo textTech

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 מצהיר על מאפיין שאינו יכול להיות null, ושמקבל ערך מאוחר יותר, לאחר יצירת האובייקט, למשל באמצעות מתודת הגדרה. קריאה שלו לפני שהוקצה לו ערך תגרום לחריגה, ואילו ::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בחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: Kotlin אונליין