Переменные, типы и null-безопасность
В Kotlin переменные объявляются через val (неизменяемые) и var (изменяемые), типы чаще выводятся автоматически, а возможность хранить null встроена прямо в систему типов.
Суть: правильный выбор между val и var и аккуратная работа с nullable-типами — это фундамент надёжного Kotlin-кода, на котором держится всё остальное.
Любая программа крутится вокруг данных, а данные живут в переменных. От того, как вы их объявляете, зависит читаемость и безопасность кода. В Kotlin есть два ключевых слова. val создаёт неизменяемую ссылку: присвоить значение можно один раз. var создаёт изменяемую переменную, которой можно присваивать новые значения. Правило хорошего тона простое: всегда начинайте с val и переходите на var, только когда значение действительно должно меняться.
val pi = 3.14159 // тип Double выведен автоматически
var counter = 0 // тип Int, значение будет меняться
counter = counter + 1 // ок, var
val city: String = "Казань" // тип указан явно
// city = "Москва" // ОШИБКА: val переприсвоить нельзяБазовые типы
Kotlin предлагает привычный набор: целые числа Int и Long, дробные Double и Float, логический Boolean, символ Char и строку String. Важная деталь: в Kotlin нет примитивов в синтаксисе — всё это объекты, у которых есть методы. При этом компилятор сам решает, где использовать эффективное примитивное представление под капотом, так что за производительность можно не волноваться.
Строки поддерживают удобную интерполяцию через $: вместо склейки плюсами вы вставляете значения прямо в текст.
val name = "Лена"
val age = 19
println("$name, $age лет") // Лена, 19 лет
println("Через год будет ${age + 1}") // выражение в фигурных скобкахКак работает под капотом
Когда вы пишете val pi = 3.14159, компилятор анализирует литерал справа и выводит тип Double. Этот механизм называется выводом типов (type inference). Он не делает язык динамическим: тип фиксируется на этапе компиляции и дальше не меняется. Просто вам не нужно писать его руками там, где он очевиден.
Nullable-типы реализованы тоже статически. Тип String и тип String? — это, с точки зрения компилятора, разные типы. Для String? компилятор отслеживает, проверяли ли вы значение на null, и не разрешает «небезопасный» доступ. Это называется smart cast: после проверки if (s != null) внутри блока переменная автоматически считается не-null.
String --> гарантированно не null String? --> может быть null s?.length безопасный вызов: null, если s == null s ?: "-" элвис: подстановка, если s == null s!!.length опасно: бросит исключение, если s == null
Операторы для null
Безопасный вызов ?. возвращает null вместо падения, если объект слева null. Оператор Элвиса ?: подставляет значение по умолчанию. Оператор !! утверждает, что значение точно не null, и бросает исключение, если ошиблись, — его стоит избегать.
fun greet(name: String?): String {
// если name == null, используем "гость"
val safe = name ?: "гость"
return "Привет, $safe"
}
println(greet("Иван")) // Привет, Иван
println(greet(null)) // Привет, гостьЧастые ошибки
Везде ставить var. Привычка из других языков делает код менее предсказуемым: любая переменная может неожиданно измениться. Иммутабельность через val упрощает чтение и отладку.
Злоупотреблять !!. Каждый !! — это потенциальный NullPointerException. Почти всегда лучше ?. с ?: или нормальная проверка.
Путать пустую строку и null. Строка "" — это не null; для проверок используйте isNullOrEmpty() или isNullOrBlank().
Best practices
- По умолчанию
val;var— только при реальной изменяемости. - Делайте тип nullable только тогда, когда отсутствие значения осмысленно.
- Предпочитайте
?:для значений по умолчанию вместо длинных if-else. - Для пустых строк используйте
isNullOrBlank(), а не сравнение с null вручную.
Логику оператора Элвиса легко прочувствовать на простом редьюсере значений по умолчанию. Запустите врезку.
# Аналог оператора Элвиса ?: из Kotlin
def elvis(value, default):
return value if value is not None and value != '' else default
inputs = ['Иван', None, '', 'Мария']
for x in inputs:
print(repr(x), '->', elvis(x, 'гость'))Попробуй сам ▶ — поменяйте значение по умолчанию и входные данные. Так же ведёт себя name ?: "гость" в Kotlin.
Итог: val и var задают изменяемость, вывод типов избавляет от лишнего шума, а nullable-типы вместе с операторами ?., ?: и smart cast делают работу с отсутствующими значениями безопасной. Эти базовые кирпичики вы будете использовать в каждом файле проекта.