История вопроса:
В Java неизменяемость коллекций достигается через обертки типа Collections.unmodifiableList() и проектирование immutable-классов ручным образом (final-поля, отсутствие сеттеров). В практике Android и серверной разработки это становилось расходным и неудобным, а безопасность потоков и предсказуемость данных терялись.
Проблема:
Java-коллекции небезопасны по умолчанию, можно всегда получить ссылку и изменять объект. Это усложняет контроль за изменяемостью, особенно в многопоточных средах, и приводит к трудноуловимым багам.
Решение:
Kotlin изначально проектировался с разделением read-only и mutable коллекций, а создание truly immutable объектов стало проще с помощью val, data class и List/Set/Map без mutable-приставки. Поверх этого — использование копирования через copy()-методы.
Пример кода:
val nums: List<Int> = listOf(1, 2, 3) // неизменяемый список // nums.add(4) // ошибка компиляции val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // Пример с неизменяемым map val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)
Ключевые особенности:
Гарантирует ли val абсолютную неизменяемость вложенного объекта?
Нет! val гарантирует только то, что переменная не может указывать на другой объект, но поля объекта можно менять (если внутренние свойства var).
Можно ли получить мутируемый объект из read-only коллекции?
Да — если коллекция содержит изменяемый объект, можно менять его состояние, несмотря на read-only интерфейс коллекции.
data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // допустимо
Что произойдет при приведении List<Int> к MutableList<Int> через as?
Оно скомпилируется, но приведет к ClassCastException во время выполнения, если исходный тип был read-only интерфейсом.
Android-приложение хранит state в val List<User>, считая его неизменяемым. Один из разработчиков модифицирует user.name. В другом фрагменте обновленный список неожиданно отображает неконсистентные данные — баг появляется в релизе.
Плюсы:
Минусы:
Использование immutable data class только с val-полями, фильтрация доступа к вложенным объектам, обновление состояния через copy().
Плюсы:
Минусы: