Search code examples
androidandroid-jetpack-composecompose-recomposition

What are benefits of @Immutable on a data class?


Looking at the documentation of Immutable, there's a code example:

@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)
        }
    }
}

and a caption:

Marking Person immutable allows calls the PersonView Composable function to be skipped if it is the same person as it was during the last composition.

My question is:

If recomposition occurs only when arguments of @Composable function change and data classes like Person in the code above (i.e. containing only primitive vals) cannot be changed without creating a new instance, then what's the optimization here?
When would the calls to the PersonView Composable function be skipped compared to the same code but without the @Immutable annotation? Is it somehow related to the mutable/unstable argument people: List<Person> of PeopleView?


Solution

  • If the Person class is declared in the same module, there is no difference (with or without the @Immutable), because Compose compiler will automatically infer that the Composable is skippable. You can check it by generating Compose compiler reports.

    However, when the Person comes from another module in a multimodule app (or from external library) and does not have the @Immutable annotation, then the Composable will not be skippable (cannot be inferred). There is an exception to the rule: if Compose compiler is enabled on the other module the stability is inferred anyway.

    Is it somehow related to the mutable/unstable argument people: List of PeopleView?

    Yes, the concept is the same, here the List interface does not guarantee immutability of the underlaying list (it may be MutableList), so by default List is not skippable and causes recomposition every state update.

    Good read: compose-api-guidelines