The Costs of Immutability

Immutability is not without its downsides. Partly, that comes from its use in languages where immutability is not an integrated feature, such as Java. And partly, that comes from environments where mutability is the norm; you may be unable to impose immutability due to environmental restrictions.

Partial Immutability Problems

In a language like Java, where immutability is not a built-in feature, you need to implement it manually, ensuring that your to-be-immutable objects lack setters or other means of manipulating their contents. On the surface, this may not seem very difficult. After all, not writing code (e.g., setters) should take less time than would actually writing that code.

However, not everything can be made immutable just by avoiding setters.

For example, suppose we have:

class FooModel {
  final String bar;
  final List<GooModel> goos;

  FooModel(String bar, List<GooModel> goos) {
    this.bar=bar;
    this.goos=goos;
  }
}

There are no setters, and both fields are final. This is immutable, right?

Actually, no, because the goos List might be mutable. If this is just an ordinary ArrayList, for example, holders of a FooModel instance can call add() or remove() on the goos field, changing its contents.

This can be improved somewhat via the Collections class and its unmodifiableList() method:

class FooModel {
  final String bar;
  final List<GooModel> goos;

  FooModel(String bar, List<GooModel> goos) {
    this.bar=bar;
    this.goos=Collections.unmodifiableList(goos);
  }
}

Now, goos will fail if you attempt to call add(), remove(), etc. on it.

However, not all collection types have a corresponding unmodifiable...() method. Plus, you need to remember to use the unmodifiable...() method, such as we do here in the constructor. And, what if GooModel is mutable? Holders of a FooModel could reach into goos, pluck out a GooModel, and change it.

Creating surface-level immutability is not that hard, even in Java. The challenge is in having immutability “all the way down”.

Some Things Want Setters

Unfortunately, some things really want setters or other forms of mutability:

In these and similar cases, avoiding mutability may be impossible, just because you are trying to use something that itself expects some degree of mutability.

Garbage, To Be Collected

One big problem with immutability is that it leads to lots of data copying. Instead of simply updating a field of a model object with a new value, we create a new instance of that model object. We cannot even use an object pool to help minimize the garbage that gets created, because typically we cannot reuse an existing object… because to reuse it, we often need to fill in different data, and that requires mutability.

The copies might be shallow copies, reducing the amount of garbage. Going back to the earlier example, we could have:

class FooModel {
  final String bar;
  final List<GooModel> goos;

  FooModel(String bar, List<GooModel> goos) {
    this.bar=bar;
    this.goos=Collections.unmodifiableList(goos));
  }

  FooModel withNewBar(String bar) {
    return(new FooModel(bar, this.goos));
  }
}

Here, we create a new FooModel, but both the old and the new instance of FooModel share the same goos collection. If goos is immutable, sharing it between two FooModel instances is not a problem. So, we consume the extra memory for an extra FooModel, but not an extra list of GooModel instances, keeping the memory consumption down.

However, there is little doubt that immutability leads to more garbage in Java. On Android 5.0+, ART’s garbage collector will help reduce the impact of this garbage, but it cannot completely eliminate its effects.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.