Menu
Coddy logo textTech

lazy と lateinit

CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 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

ブロックは一度だけ実行されます。上記の 2 回目の読み取りでは、保存された結果が使用されました。すべての読み取りで実行されるカスタム 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

構築時に値がわかっている場合は、通常の初期化を優先してください。不要になる可能性がある、コストの高いvalにはlazyを使用し、値が本当に後から設定されるvarにのみlateinitを使用してください。lateinitはInt、Boolean、その他のプリミティブ型では機能しません。

val config by lazy { loadConfig() }     // 初回使用時に一度だけ計算される
lateinit var user: User                 // 構築後に代入される
// lateinit var count: Int              // error: プリミティブ型では許可されていません
challenge icon

チャレンジ

簡単

Document(val text: String) クラスを完成させてください。単一のスペースで区切られた単語数を数え、計算するたびに computations に 1 を加えて記録する lazy プロパティとして wordCount を追加し、作成者が設定されていない場合は by Ada または by unknown を返すメソッド credit() を持つ lateinit var author: String も追加してください。

提供されているコードはテキストを読み取り、wordCount を 3 回読み取り、2 行目が存在する場合は作成者を設定し、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プレイグラウンド