Шаг 160.
Основы Kotlin.
Инициализация. Отложенная инициализация

    На этом шаге мы рассмотрим назначение и организацию такой инициализации.

    Поздняя инициализация - не единственный способ задержать инициализацию. Можно задерживать инициализацию переменной до первого обращения к ней. Эта идея известна как отложенная инициализация, и, несмотря на свое название, она может сделать код более эффективным.

    Большая часть свойств, которые вы инициализировали в предыдущих шагах, были довольно простыми. Например, одиночные объекты String. Однако многие классы образуют более сложную систему. Они могут требовать создания нескольких объектов или выполнять сложные вычисления при инициализации, например читать данные из файла. Если для инициализации вашего свойства требуется выполнять подобные вычисления или класс не требует немедленной готовности свойства, используйте отложенную инициализацию.

    Отложенная инициализация реализована в Kotlin через механизм делегатов. Делегат определяет шаблон инициализации свойства.

    Определение делегата осуществляется с помощью ключевого слова by. Стандартная библиотека Kotlin включает несколько готовых делегатов, например lazy. Делегат lazy принимает лямбда-выражение с кодом, который вы бы хотели выполнить для инициализации свойства.

    Начальное значение для свойства hometown в Player извлекается из файла. Вам не нужно обращаться к hometown сразу, поэтому инициализировать его следует при первом обращении. Реализуем отложенную инициализацию hometown в Player.


Некоторые из этих изменений сложно заметить. Надо удалить =, добавить by lazy и фигурные скобки вокруг selectHometown().
import java.io.File

class Player (_name: String,
              var healthPoints: Int = 100,
              val isBlessed: Boolean,
              private val isImmortal: Boolean) {
    var name = _name
        get() = "${field.capitalize()} of $hometown"
        private set(value) {
            field = value.trim()
        }
    init {
        require(healthPoints > 0, { "healthPoints must be greater than zero." })
        require(name.isNotBlank(), { "Player must have a name." })
    }

    val hometown by lazy { selectHometown() }

    constructor(name: String) : this(name,
            isBlessed = true,
            isImmortal = false) {
        if (name.toLowerCase() == "kar") healthPoints = 40
    }

    private fun selectHometown() = File("data/towns.txt")
            .readText()
            .split("\n")
            .shuffled()
            .first()
}


Рис.1. Отложенная инициализация hometown (Player.kt)

    Это лямбда-выражение возвращает результат вызова selectHometown(), который затем присваивается hometown.

    Свойство hometown остается неинициализированным до первого обращения к нему. В этот момент выполнится код лямбды в lazy. Обратите внимание, что этот код выполнится только один раз, при первом обращении к делегированному свойству в методе чтения свойства name. Все дальнейшие обращения к этому свойству будут использовать ранее полученный результат вместо повторных вычислений.

    Отложенная инициализация полезна, но она слишком многословна, поэтому используйте ее только тогда, когда требуются сложные вычисления.

    Подводя итоги, отметим, что вы узнали все об инициализации объектов в Kotlin. Чаще всего вы будете использовать самый простой способ, вызывая конструктор и получая ссылку на экземпляр класса для дальнейшей работы. Но у вас есть и другие варианты инициализации объектов в Kotlin, и понимание этих вариантов поможет вам писать более красивый и эффективный код.

    В следующих шагах вы познакомитесь с наследованием, то есть с принципом объектно-ориентированного программирования, который позволяет родственным классам совместно использовать данные и поведение.

    На следующем шаге мы рассмотрим подводные камни инициализации.




Предыдущий шаг Содержание Следующий шаг