Глядя на документацию Immutable, есть пример кода:
@Immutable
data class Person(val name: String, val phoneNumber: String)
@Composable
fun PersonView(person: Person) {
Column {
Row {
Text("Name: ")
Text(person.name)
}
Row {
Text("Phone: ")
Text(person.phoneNumber)
}
}
}
@Composable
fun PeopleView(people: List<Person>) {
Column {
for (person in people) {
PersonView(person)
}
}
}
и подпись:
Пометка Person immutable позволяет пропускать вызовы Composable функции PersonView, если это тот же человек, что и во время последней композиции.
Мой вопрос:
Если рекомпозиция происходит только тогда, когда изменяются аргументы функции @Composable, а классы данных, такие как Person в приведенном выше коде (т.е. содержащие только примитивные vals), не могут быть изменены без создания нового экземпляра, то в чем тут оптимизация?
Когда вызовы функции PersonView Composable будут пропущены по сравнению с тем же кодом, но без аннотации @Immutable? Это как-то связано с изменяемым/нестабильным аргументом people: List<Person> из PeopleView?
Если класс Person объявлен в том же модуле, нет никакой разницы (с @Immutable или без него), потому что компилятор Compose автоматически сделает вывод, что Composable можно пропустить. Вы можете проверить это, сгенерировав отчеты компилятора Compose.
Однако, когда Person поступает из другого модуля в многомодульном приложении (или из внешней библиотеки) и не имеет аннотации @Immutable, то Composable нельзя будет пропустить (нельзя вывести). Есть исключение из правила: если в другом модуле включен компилятор Compose, стабильность в любом случае предполагается.
Это как-то связано с изменяемым/нестабильным аргументом people: List of PeopleView?
Да, концепция та же, здесь интерфейс List не гарантирует неизменности нижележащего списка (может быть MutableList), поэтому по умолчанию List не пропускаем и вызывает перекомпоновку при каждом обновлении состояния.
Полезно прочитать: compose-api-guidelines