Шаг 146.
Основы Kotlin.
Объявление классов. Защита от состояния гонки

    На этом шаге мы рассмотрим что это за состояние и как его избежать.

    Если свойство класса одновременно изменяемое и имеет тип с поддержкой null, убедитесь, что оно не равно null, прежде чем ссылаться на него. Например, рассмотрим следующий код, который проверяет, есть ли у игрока оружие (ведь игрок может быть безоружен или разоружен). Если оружие есть, выводится его название:

class Weapon(val name: String)
class Player {
    var weapon: Weapon? = Weapon("Ebony Kris")
    fun printWeaponName() {
        if (weapon != null) {
            println(weapon.name)
        }
    }
}
fun main() {
    Player().printWeaponName()
}

    Вы удивитесь, но этот код не компилируется. Посмотрите на ошибку ниже, чтобы понять почему (рисунок 1).


Рис.1. Умное приведение типа неприменимо к Weapon

    Компилятор прерывает компиляцию кода из-за возможности состояния гонки. Состояние гонки возникает, если есть вероятность параллельного изменения состояния из другого места в программе. Это может привести к непредвиденным результатам.

    В приведенном выше примере компилятор видит, что хотя weapon и проверяется на неравенство значению null, все равно существует вероятность, что свойство weapon класса Player получит значение null между моментом проверки и вывода названия оружия на экран.

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

    Исправить эту проблему и защитить себя от null можно с помощью стандартной функции also:

class Player {
    var weapon: Weapon? = Weapon("Ebony Kris")
    fun printWeaponName() {
        weapon?.also {
            println(it.name)
        }
    }
}


Рис.2. Пример использования функции also

    Этот код компилируется благодаря стандартной функции also. Вместо ссылки на свойство класса код использует аргумент it функции also, который в данном случае является локальной переменной, существующей только внутри области видимости анонимной функции. Это означает, что переменная it гарантированно не будет изменена другими частями программы. Нам удалось избежать проблемы с умным приведением типа, так как вместо использования свойства с поддержкой null этот код использует локальную переменную, доступную только для чтения и не поддерживающую null. Эта переменная является локальной, потому что also вызывается после оператора безопасного вызова weapon?.also.

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




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