Начнем с базового примера:
data class SomeInfo(
var count: Int,
var someList: MutableList<Int>
)
Вроде бы обычный объект, что может пойти не так? Основная проблема этого объекта, что в любой момент можно заменить число, ссылку на список или сам список. Если вы передаете этот объект в какую-нибудь функцию, а потом еще куда-то, можно получить не консистентное состояние объекта. Как это может произойти: у вас было 3 числа в списке, и вы ожидаете что там будет 3 числа, а тут бац, а список пустой. Потому что какая-то функция не была чистой, она сначала сохранила ссылку на объект, а потом изменила состояние этого объекта.
Теперь рассмотрим тот же объект:
data class SomeInfo(
val count: Int,
val someList: List<Int>
)
В этом случае мы защищены от любых изменений, можно передавать этот объект куда хочется, даже между потоками, ничего не произойдет. Возникает вопрос, что делать если нужно поменять что-то в этом объекте? Все просто, тупо создаем новый, сильно много памяти вы не скушаете, Android начиная с 7 версии очень шустро умеет избавляться от таких объектов. Про преждевременную оптимизацию писал в предыдущем посте.
Эту концепцию можно расширить не только на data class, но и в других классах бизнес логики. Если можно избавится от переменных нахер их. Все коллекции в data class делаем неизменяемыми, да и вообще все в data class лучше делать неизменяемыми. Изменяемыми коллекциями можно пользоваться только в рамках одного класса, а если отдаете список наружу, то уже отдавайте только неизменяемый.