← Назад к вопросам

Всегда ли переменная созданная с помощью val возвращает одно и то же значение?

1.6 Junior🔥 201 комментариев
#Kotlin основы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Краткий ответ

Нет, переменная, объявленная с помощью val (immutable reference — неизменяемая ссылка) в Kotlin, не всегда возвращает одно и то же значение. Ключевое слово val гарантирует, что сама ссылка на объект не может быть изменена после инициализации, но это не означает, что внутреннее состояние объекта (его поля или свойства) не может измениться. Это фундаментальное различие между immutable reference (неизменяемая ссылка) и immutable object (неизменяемый объект).

Подробное объяснение

Что гарантирует val?

При объявлении переменной с val компилятор Kotlin обеспечивает, что этой переменной можно присвоить значение только один раз — при инициализации. Попытка переприсвоения приведёт к ошибке компиляции.

val name = "Alice"
name = "Bob" // Ошибка компиляции: Val cannot be reassigned

Однако, если переменная хранит ссылку на изменяемый (mutable) объект, то содержимое этого объекта может быть изменено, даже если сама ссылка остаётся постоянной.

Пример с изменяемым объектом

Рассмотрим класс Person с изменяемым свойством age.

class Person(var name: String, var age: Int)

fun main() {
    // Ссылка `person` неизменна, но объект изменяем
    val person = Person("Alice", 25)
    println("${person.name}, ${person.age}") // Alice, 25

    // Меняем внутреннее состояние объекта
    person.age = 26
    person.name = "Alicia"
    
    println("${person.name}, ${person.age}") // Alicia, 26
    // `person` по-прежнему ссылается на тот же объект в памяти
}

Здесь переменная person объявлена как val, но её свойства name и age были изменены, потому что класс Person использует var для этих полей. Ссылка осталась прежней, а значение, возвращаемое при обращении к свойствам объекта, изменилось.

Пример с коллекциями

Это особенно важно при работе с коллекциями. Kotlin разделяет интерфейсы на изменяемые (MutableList, MutableSet, MutableMap) и неизменяемые (List, Set, Map).

fun main() {
    // `val` + неизменяемый список
    val immutableList = listOf(1, 2, 3)
    // immutableList.add(4) // Ошибка: нет метода add
    // immutableList[0] = 5 // Ошибка: не поддерживает оператор set

    // `val` + изменяемый список
    val mutableList = mutableListOf(1, 2, 3)
    println(mutableList) // [1, 2, 3]
    
    mutableList.add(4)    // Изменяем содержимое объекта
    mutableList[0] = 99   // Меняем элемент
    println(mutableList) // [99, 2, 3, 4]
}

В случае с mutableList переменная объявлена как val, но сам объект MutableList изменяем, поэтому его содержимое может меняться.

Когда val возвращает одно и то же значение?

Это происходит только когда переменная val ссылается на истинно неизменяемый объект (immutable object). В Kotlin к таким объектам относятся:

  • Примитивные типы (Int, Double, Boolean и т.д.).
  • Строки (String) — в Kotlin они неизменяемы.
  • Неизменяемые коллекции (List, Set, Map), созданные с помощью listOf(), setOf(), mapOf() (если они не содержат изменяемых элементов).
  • Data-классы, все свойства которых объявлены как val, и которые не содержат ссылок на изменяемые объекты.
  • Объекты, специально спроектированные как immutable (все поля — val или private val без сеттеров).
// Пример полностью неизменяемого объекта
data class ImmutablePoint(val x: Int, val y: Int)

fun main() {
    val point = ImmutablePoint(10, 20)
    // point.x = 5 // Ошибка: val нельзя переприсвоить
    // Всегда возвращает (10, 20)
}

Делегированные свойства и by lazy

Также стоит отметить случай с ленивой инициализацией by lazy. Переменная val может быть вычислена при первом обращении, и тогда её значение будет закэшировано.

val lazyValue: String by lazy {
    println("Вычисляю значение")
    "Результат"
}

fun main() {
    println(lazyValue) // "Вычисляю значение" затем "Результат"
    println(lazyValue) // Только "Результат" (значение взято из кэша)
}

Здесь lazyValueval, и после первого обращения она действительно всегда возвращает одно и то же вычисленное значение. Это исключение, подтверждающее правило: val гарантирует, что после инициализации значение не меняется, а by lazy откладывает эту инициализацию до первого использования.

Итог

  • val гарантирует неизменяемость ссылки, а не объекта.
  • Если объект изменяем (mutable), то его состояние может меняться даже при val переменной.
  • Для полной неизменяемости нужна комбинация: val + неизменяемый объект (immutable object).
  • Понимание этой разницы критически важно для написания корректного многопоточного кода, предотвращения случайных изменений состояния и соблюдения принципов функционального программирования в Kotlin.