Java Immutability Visualised

syIsTyping
don’t code me on that
4 min readOct 12, 2019

In Java, mutability is the default; we are always able to reassign variables to point to something else. Mutability is not always ideal and to make a variable immutable, we’d have to explicitly declare it. There are varying levels of immutability and it’s easier to understand it by visualising what is going on.

There is a difference between immutable and read-only. Read-only means that the variable/attribute/object cannot be reassigned and does not expose methods to mutate itself. Immutability is satisfied when the variable/attribute/object is read-only, and all others it references and/or contains are read-only as well, recursively. I use both terms here interchangeably unless otherwise stated.

Let’s start with the simplest, a variable pointing to a primitive type. Here we have a mutable variable foo pointing to an int. (The circle represents a primitive type)

int foo = 42;

To make foo immutable, we only have to add the final keyword. It then cannot be reassigned. (We’ll use thick borders to represent that)

final int foo = 42;

But what happens when foo is not pointing to a primitive type but to an object? For instance, a Foo class that contains an int? In this case, even though foo cannot be reassigned, value in the class is mutable, and can be reassigned.

class Foo {
int value;
Foo(int value) { this.value = value; };
}
final Foo foo = new Foo(42);

But you already knew how to make a variable immutable, right? Just add final to value and it becomes immutable too.

class Foo {
final int value;
Foo(int value) { this.value = value; };
}
final Foo foo = new Foo(42);

Now suppose instead of one Foo, you have a List of them. In Java, Arrays.asList andArrayList are also mutable by default. So even though value in Foo cannot be reassigned, the reference in the list can be.

class Foo {
final int value;
Foo(int value) { this.value = value; };
}
final List<Foo> foo = Arrays.asList(new Foo(42));

To obtain an immutable List, we can use either Guava’s ImmutableList, Apache Collection Commons’ ListUtils.unmodifiableList, or Java’s Collections.unmodifiableList. Note that there are subtle differences.

class Foo {
final int value;
Foo(int value) { this.value = value; };
}
final List<Foo> foo = ImmutableList.of(new Foo(42));

Now let’s focus on Foo class. If Foo has an attribute that points to a non-primitive type, it has to either ensure that that type is immutable (for eg, String), or keep an immutable copy of it. For example, if has a List attribute, we have to keep a copy of the list passed in. A great framework for creating immutable classes is Immutables.

class Foo {
final List<String> values;
Foo(List<String> values) {
this.values = ImmutableList.copyOf(values);
};
}
final List<Foo> foo = ImmutableList.of(new Foo(/* list */));

Lastly, we should make the Foo class final, as a best practice for immutability. With this, we have finally achieved immutability^. A recap of what we had to do:

  • Declare all variables as final
  • Use immutable versions of any collections we use
  • Declare all class attributes as final
  • Ensure attributes are immutable types
  • If there are mutable type attributes, make copies of them
final class Foo {
final List<String> values;
Foo(List<String> values) {
this.values = ImmutableList.copyOf(values);
};
}
final Foo foo = new Foo(ImmutableList.of("hello world"));

^ Note: except for Reflection.

Why Immutability?

Other than this list of benefits (including thread-safety), since immutable objects have a fixed state, it is easier to reason about them. Therefore using immutable objects liberally makes the code easier to understand and reduces cognitive load.

Immutability in Kotlin

In Kotlin, immutability is the default and mutability has to be explicitly declared. Some examples:

  • To declare an immutable variable, declare as val. This is equivalent to final in Java. Mutable variables are declared as var.
  • This is the same for class attributes.
  • Kotlin distinguishes read-only* and mutable collections. List is read-only and the method to create a list listOf() returns a read-only list.
  • Classes cannot be extended by default ie, final.

The snippet of code above would look like this in Kotlin:

class Foo(values: List<String>) {
val values = values.toList()
}
val foo = Foo(listOf("hello world"))
  • read more about it here

--

--

syIsTyping
don’t code me on that

Security engineer and new dad in Japan. I've learnt a lot from the community, so I hope to contribute back. I write technical articles and how-to guides.