Immutability

Generally, developers like setters.

After all, it stands to reason that if you can get a piece of data from an object, you should be able to modify that piece of data in that same object. We are used to read-write data structures, whether in memory or persisted.

However, there are some costs to allowing objects to be mutable (in other words, able to be modified). In some cases, you can have a more robust app architecture if you consider objects to be immutable and unable to be modified. The objects get replaced outright, rather than changing their contents. This winds up being a bit more reminiscent of a transactional database, where changes are applied as a unit, rather than piecemeal.

In this chapter, we will explore the benefits (and costs) of immutability and how to create immutable (or mostly immutable) objects in Java.

The Benefits of Immutability

Having immutable objects — particularly for things like models — is not a new concept. Immutability has had its adherents for quite some time. It is what lead to libraries like AutoValue for Java, and immutability features in languages like Kotlin.

So, why go with immutability?

No Dirty Data

A perpetual problem with model objects (and related objects, such as view-models), is knowing what data changed, when, and by what. That comes from promiscuous use of setters, blindly changing data that might be in use already.

For example:

Now, we have possible data consistency issues:

Immutable model and view-model objects do not prevent this sort of situation, but they help to make it a bit more obvious. Changing the state is a more obvious action; it is not merely a matter of calling some setters.

Thread Safety

If more than one thread has access to the same data, and that data can change, we wind up having to synchronize access to that data, so that changes can be made atomically. We do not want a thread to be part-way through updating the data when another thread tries reading it, as the partly-updated data may be in an inconsistent state at that moment. We wind up using synchronized and CopyOnWriteArrayList and all sorts of other constructs to allow mutable data to be shared between threads.

This goes away if the data is immutable. Multiple threads can read the same, unchanging data whenever they want without issue. Now, we limit our synchronization to more specific scenarios, such as updating shared caches: so long as we are replacing a cache entry atomically, all consumers of the cache can run in parallel, if the cache entries themselves are immutable.

Functional Programming

One way to combat the complexities of multi-threaded development is to use functional programming. Functional programming is based on pure functions: methods (or the equivalent) that operate solely on input parameters, with no side-effects that affect the operation of the program.

RxJava is based on functional programming concepts. We build up chains of RxJava operators, where each operator applies some sort of function to the input, such as the map() operator applying a Function to convert objects from one type to another.

Immutability is one way of imposing a contract upon yourself, as a developer, to avoid side effects. Calling a setter is a very casual act in programming, even if calling that setter introduces a side effect. Immutability enforces the creation of new objects, ideal for use in pure functions, where the function can create objects to return but cannot change the parameters’ contents and cause side effects.


Prev Table of Contents Next

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