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

Блок выполняется один раз: при втором чтении выше использовался сохранённый результат. В отличие от пользовательского геттера, который выполняется при каждом чтении, 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              // error: не допускается для примитивных типов
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