Throughout this book, we have favored the terms "read-only" over "immutable," with few exceptions – but we have not explained why. Now is the time. “Immutable” means “unchangeable,” and we think it is a misleading label for Kotlin collections (and certain other types) because they can, indeed, change. Let’s look at some examples using lists.
Here are declarations of two Lists. They are read-only – declared with val
. The element each one happens to contain is a mutable list.
val x = listOf(mutableListOf(1,2,3)) val y = listOf(mutableListOf(1,2,3)) x == y true
So far, so good. It appears that x and y were assigned with the same value, and the List API does not expose any functions for adding, removing, or reassigning a particular element.
However, the lists contain mutable lists, and their contents can be modified:
val x = listOf(mutableListOf(1,2,3)) val y = listOf(mutableListOf(1,2,3)) x[0].add(4) x == y false
The structural comparison between x and y now evaluates as false, because the contents of x mutated. Should an immutable (“unchangeable”) list behave this way? In our opinion, it should not.
Here is another example:
var myList: List<Int> = listOf(1,2,3) (myList as MutableList)[2] = 1000 myList [1, 2, 1000]
In this example, myList was cast to the MutableList type – meaning that the compiler was instructed to treat myList as a mutable list, despite the fact that it was created with listOf. (You will read about casting in depth in Chapter 14 and Chapter 16.) This cast has the effect of allowing a change to the value of the third item in myList. Again, not the behavior we expect of something labeled “unchangeable.”
A List in Kotlin does not enforce immutability – it is up to you to use it in an immutable fashion. A Kotlin List’s “immutability” is only skin deep – and whatever you wind up calling it, remember that.