Object immutability pattern — the only constant in the ever-changing world of programming

Przemysław Żochowski
4 min readApr 26, 2021

Software craftmanship has been growing for a relatively long period of time and throughout that time, we (as a whole community) happened to come up with a lot of good practices called design patterns. Those patterns force us to do something in a specific way (like a recipe to make a perfect meal), but they bring us some benefits as a reward. Generally they help us stick to the SOLID principles, but it is important to be aware of sacrifice vs win ratio. In other words, we need to be aware of what a given design pattern requires us to do and what it will give us in return. One of such design patterns tells us about immutability.

Important note!

This article has on aim familiarizing you with the topic of immutable objects, their benefits and potential disadvantages. Everything is based on my experience with Java, but I guess the rules can be applied to any OOP language.

What does it mean to create immutable objects?

An immutable object is one that does not change its state after being created in the constructor. It’s like a piece of liquid iron that has been molded using a pre-set form. Once it is created you can not change its shape.

How do we create an immutable objects?

There are several steps to make an object immutable.

  1. Create a class with private access fields — this way we prevent a direct access to the fields from outside the class, we encapsualte the data within the class;
  2. Mark all the fields as final ones — this prevents us from any change of the values assigned to the class fields;
  3. Instantiate the object fully in the constructor;
  4. Do not incorporate any mutators (no setters in the class);
  5. Mark the class as a final one — thanks to that, nobody will be able to inherit from our class and change its behaviour;
  6. In case you provide any method that changes the state of an object, it should return a new instance of that object with changed values;
  7. In case you incorporate a mutable object field in your immutable class (through a composition):
  • always make a deep copy of that object to break the reference connection and create a fully independent copy of that object;
  • when exposing getters also make sure to return a deep copy of that object;

The last point is a vital one and a bit cumbersome, especially when the mutable object we pull into our immutable class has a deep network of other mutable objects. If we didn’t do that however, we would end up with our class being exposed to mutability via reference. In other words, if we assigned the mutable object passed in the constructor to our field, every state change of that object, would be reflected on our field, thus breaking the rule of immutability.

Additionally and optionally, you could make your constructor private and expose static factory methods. The benefit here is two-fold:

a) you block creation of your object from outside of the class via constructor;

b) it gives us a possibility to provide more meaningful name to a method that facilitates object creation;

What are the benefits of using immutable objects?

This pattern comes with plenty of benefits.

  1. Thread-safety — we are 100% sure that every thread accessing the object operates on the same, immutable version of the object;
  2. Foundation of functional programming — in the functional programming paradigm, we create values or objects by initializing them. Then we use them, but we do not change their values or their state. If we need, we create a new one, but we do not modify the existing object’s state;
  3. Foundation of Domain Driven Design — the basic build block in DDD architecture is so called value object. The basic requirement of this construct is immutability;
  4. Easier testing and bug finding — as objects don’t change their state, the majority of the tests related to them follow the principle “given X as input, we should get Y as output”;
  5. Ease of caching — we can enhance the speed and efficiency of our application by caching immutable objects. If you know how the String pool works in Java, then that’s exactly what kind of optimisation I mean;
  6. Good fit for key in HashMap — changing the value of an object might change the generated hashcode value. Therefore such an object might get lost in standard collections, such as Set or HashMap. Making the object immutable defends us against that danger;

What are the downsides of using immutable objects?

  1. Sometimes cumbersome implementation — as mentioned earlier, if our immutable class contains a mutable object field (and possibly the whole tree of mutable objects underneath), you need to make a deep copy in the constructor. Moreover, we need to instantiate the object fully in the moment of creation, which in case of big, complex objects might be a hassle;
  2. Higher memory consumption — since every time we want to change a state of immutable object, we create a new one instead. However, at least in the Java world, the impact of that downside is lowered by the fact that JVM garbage collector in the latest versions of Java is specialized in cleaning short-living objects. There is so called “escaped analysis” going on behind the scenes.

Summary

Immutability is a very useful and desired feature of our objects — after all it ensures predictability and stability of our code. As a strong advocate of tests and functional programming paradigm, I can’t stress enough the importance of having immutable objects on board, when starting any new project in Java.

--

--

Przemysław Żochowski

Passionate Java full-stack developer. Strong advocate of the motto: "If you can't explain something in simple words, you don't understand it well enough"