Как защититься от ошибок при изменении порядка свойств в data class Kotlin, используемых для деструктуризации
Что проверяют
Понимаете ли вы, что деструктуризация в Kotlin позиционная и опирается на сгенерированные componentN(), а не на имена свойств. Отсюда реальная ошибка: поменяли местами два поля одного типа, код скомпилировался, данные перепутались. Следом спрашивают, где в Android-проекте такое встречается и как себя обезопасить.
Как отвечать
Для data class компилятор генерирует component1(), component2() и далее в порядке свойств первичного конструктора. Запись val (name, email) = user означает «первое и второе свойство», имена переменных тут ничего не значат. Если в классе поменять местами email и name типа String, компилятор промолчит.
Способы защиты:
- не деструктурировать широкие классы: для двух-трёх полей с очевидным порядком это читается хорошо, для остального надёжнее
user.name; - разные типы для разных смыслов:
@JvmInline value class Email(val value: String)и такой же тип для имени. Перестановка сразу даёт ошибку компиляции, а объект в большинстве случаев даже не создаётся; - осторожность с публичным API: порядок свойств data class из модуля или библиотеки становится частью контракта, его изменение ломает чужой код;
_для ненужных позиций:val (id, _, status) = orderчестно показывает, что порядок важен;- тесты на маппинг: там, где DTO превращается в доменную модель, тест с разными значениями полей сразу поймает перестановку.
Типичные места в Android: лямбды вроде map { (id, title) -> ... } при разборе ответа сети, for ((key, value) in map) и Pair из ViewModel. Деструктуризация состояния Compose val (value, setValue) = remember { mutableStateOf("") } безопасна: порядок там задан интерфейсом MutableState, а не полями вашего класса.
Где спотыкаются
Отвечают, что спасают именованные аргументы. Они защищают вызов конструктора и copy(), а на деструктуризацию не влияют вовсе.
Меняют порядок полей «для красоты» и не ищут деструктуризации по проекту. Ошибка всплывает как неверные данные на экране, а не как падение.
Возвращают из функций Triple<String, String, Int> и разбирают результат по позициям. Это та же проблема без имён вовсе, своя data class надёжнее.