Kotlin const vs val: в чем разница и что выбрать?
Вы пишете код на Kotlin и вдруг задумываетесь: «А в чем, собственно, разница между const и val? Оба ведь вроде бы неизменяемые?». Отличный вопрос! Этот момент сбивает с толку даже опытных разработчиков, переходящих с Java или других языков. Неудивительно, что на Stack Overflow тема «What is the difference between "const" and "val"?» набирает тысячи просмотров. Сегодня мы раз и навсегда разберемся в этом вопросе, разложим по полочкам теорию и подкрепим все живыми примерами кода.
Готовы? Поехали!
Краткий ликбез: что такое val?
Начнем с самого простого. val (от слова value — значение) — это неизменяемая ссылка. Вы можете присвоить ей значение один раз и больше никогда не менять. Звучит знакомо? Да, это аналог final в Java или readonly в C#.
val name: String = "Алиса"
name = "Боб" // Ошибка компиляции! Val cannot be reassigned
Но есть важный нюанс: val гарантирует, что ссылка не изменится, но не гарантирует неизменность самого объекта. Если вы используете val с изменяемым списком (MutableList), вы все еще можете добавлять и удалять элементы.
val numbers = mutableListOf(1, 2, 3)
numbers.add(4) // Работает!
// numbers = mutableListOf(5, 6) // Ошибка! Нельзя переназначить ссылку
val может быть инициализирован как во время объявления, так и позже в коде (например, в конструкторе или блоке init). Его значение вычисляется во время выполнения программы (runtime).
А что такое const? Строгий страж времени компиляции
const — это более строгий брат val. Он тоже неизменяемый, но с суперспособностью: значение должно быть известно на этапе компиляции (compile-time). Другими словами, это настоящая константа, которую компилятор может подставить прямо в код, как встроенную.
У const есть жесткие правила:
- Может быть объявлен только на уровне файла (вне класса) или внутри
object/companion object. - Должен быть примитивного типа или типа
String. - Не может быть результатом вызова функции или вычисления — только литерал или другая константа.
// Правильно
const val PI = 3.14159
const val APP_NAME = "MyApp"
// Неправильно
const val currentTime = System.currentTimeMillis() // Ошибка! Значение не известно на этапе компиляции
const val userName = getName() // Ошибка! Вызов функции недопустим
Главные отличия: const vs val в таблице
Чтобы было наглядно, вот сравнительная таблица:
| Критерий | const val | val |
|---|---|---|
| Время инициализации | На этапе компиляции | На этапе выполнения |
| Где можно объявить | Топ-уровень, object, companion object | Где угодно (внутри класса, функции, конструктора) |
| Типы данных | Только примитивы и String | Любые типы (включая объекты, списки) |
| Производительность | Максимальная (инлайнинг) | Стандартная |
| Использование в аннотациях | Да | Нет |
Практический пример: аннотации и производительность
Одно из самых частых применений const — это аннотации. В Kotlin параметры аннотаций должны быть известны на этапе компиляции. Попробуйте использовать val — и компилятор выдаст ошибку.
// Так работает
const val API_ENDPOINT = "https://api.example.com"
@Deprecated(API_ENDPOINT) // OK
// Так не работает
val apiEndpoint = "https://api.example.com"
@Deprecated(apiEndpoint) // Ошибка! 'apiEndpoint' is not a compile-time constant
Еще один бонус const — производительность. Компилятор встраивает значение константы прямо в байт-код, а не создает поле класса. Это особенно заметно, если константа используется много раз. Пример:
// Kotlin код
const val MAX_RETRIES = 3
fun retry() {
repeat(MAX_RETRIES) { ... }
}
// После компиляции в Java будет что-то вроде:
// repeat(3) { ... } — значение подставлено напрямую
Когда использовать const, а когда val?
Вот простая шпаргалка:
- Используйте
const val, если:- Значение известно до запуска программы (например, математические константы, URL-адреса, названия приложений).
- Константа используется в аннотациях.
- Вы хотите максимальную производительность и минимизировать накладные расходы.
- Используйте
val, если:- Значение вычисляется во время выполнения (результат функции, ввод пользователя, текущее время).
- Константа находится внутри класса или функции.
- Тип данных не примитив и не String (например, объект, список, Map).
Жизненный пример: приложение для заметок
Представьте, что вы пишете простое приложение для заметок на Kotlin. Где пригодятся const и val?
// Константы на уровне файла — идеально для const
const val DATABASE_NAME = "notes.db"
const val MAX_TITLE_LENGTH = 50
class Note(val title: String, val content: String) {
// Это val — значение задается в конструкторе, но не const
val createdAt: Long = System.currentTimeMillis()
companion object {
// Константа внутри companion object — тоже const
const val NOTE_TYPE = "text"
}
fun validate(): Boolean {
// Локальная val — вычисляется на ходу
val isValid = title.length <= MAX_T