Как работает метод copy в дата классе Kotlin
Что проверяют
Знаете ли вы, что генерирует компилятор для data class, и понимаете ли, что copy делает поверхностную копию. В Android-проекте следом спрашивают, как copy используется при обновлении состояния экрана и почему после него иногда не обновляется UI.
Как отвечать
Для data class User(val name: String, val age: Int) компилятор генерирует аналог fun copy(name: String = this.name, age: Int = this.age) = User(name, age). Отсюда все свойства метода:
- только первичный конструктор: копируются лишь объявленные в нём свойства. Свойство из тела класса в копии инициализируется заново своим инициализатором;
- вызов конструктора:
copyсоздаёт объект через первичный конструктор, поэтому блокиinitи проверкиrequireвыполняются и на копии; - поверхностность: ссылки копируются как есть. Если в классе лежит
MutableList, копия и оригинал делят его, и изменение через одну ссылку видно через другую; - именованные аргументы:
user.copy(age = 31)меняет одно поле и оставляет остальные. Позиционные тоже допустимы, но ломаются при смене порядка свойств; - нельзя переопределить: явная реализация
copyвdata classзапрещена.
В Android это основной способ обновлять неизменяемое состояние: _state.update { it.copy(isLoading = false, items = result) } во ViewModel с MutableStateFlow. StateFlow сравнивает новое значение со старым через equals, а сгенерированный equals сравнивает поля первичного конструктора.
Kotlin постепенно приводит видимость copy к видимости конструктора: при private-конструкторе публичный copy сначала даёт предупреждение, поведение регулируют аннотации @ConsistentCopyVisibility и @ExposedCopyVisibility.
Где спотыкаются
Называют copy глубокой копией. Вложенные изменяемые объекты общие, и правка списка в копии тихо меняет оригинал.
Меняют MutableList внутри состояния и отдают it.copy(items = it.items). Список тот же, equals вернёт true, StateFlow новое значение не выпустит, и экран не обновится.
Забывают, что свойства из тела класса не участвуют ни в copy, ни в equals, ни в toString, и удивляются, что их значение «сбросилось» в копии.