Final vs Immutable
There seems to be some confusion around the concept of immutability and how to implement it in Java. In this post I will tell you how to do it, how to make use of it and try to give you a better understanding of both immutability and the keyword final.
Immutable #
So, what does the term immutable actually imply? According to Merriam-Webster the word implies something that is not capable or susceptible to change. In loose translation, with our context in mind, it means a Java object that cannot be changed after its creation. That in turn, implies that the bean has read-only properties, that it cannot be extended or in any other way be changed after the constructor has completed. When an object has these qualities, it implements what is known as the immutable pattern.
The benefits of immutable #
So now that we know what immutable actually means, what are the pros and cons? The first, kind of obvious one is thread safety. If you think of it, it makes sense. The object cannot change after it has been created, so regardless of what thread is accessing the object at hand, it cannot be changed. The first con also become quite evident, the object is of course not mutable, so you cannot change it if you want to. Immutable objects can also be more memory efficient for the JVM to process, but this depends on the implementation of the immutable object. Immutable does not automatically mean that it will be memory efficient.
Final #
Okay, time to bring in the final
keyword and explain what it does. Finality in Java is the key to implementing immutability, by adding it to any property, method or class, you’re telling the compiler to make the specific thing read-only after creation time.
Implementing immutable #
To make an entire object immutable, you need to consider in what way an object can change and apply the final keyword to the correct places. The first and obvious place to put it would be before all the class properties. In order to protect the object from being extended, you should add the keyword to the class itself, making it impossible to extend. If the methods need to be immutable, the methods of course need to have the final
keyword applied to them as well.
Pro tip #
Making the class final will make the methods implicitly final as well, since the class cannot be extended.
A simple example #
public final class Rectangle {
private final int height;
private final int width;
public Rectangle(int height, int width) {
this.height = height;
this.width = width;
}
public final getHeight() {
return this.height;
}
public final getWidth() {
return this.width;
}
}
A more complex example #
public final class Address {
private final StreetAddress streetAddress;
private final ZipCode zipcode;
private final City city;
public Address(StreetAddress streetAddress, ZipCode zipCode, City, city) {
this.streetAddress = streetAddress.deepCopy();
this.zipCode = zipCode.deepCopy();
this.city = city.deepCopy();
}
public final getStreetAddress() {
return this.streetAddress;
}
public final getZipCode() {
return this.zipCode;
}
public final getCity() {
return this.city;
}
}
Here you see something new in the constructor, the deepCopy
method and you may wonder why. It wasn’t needed in the first example, so why is it needed here? The answer is that the Rectangle class used primitive data types. The Address class however uses complex data types and as such have reference properties instead of value properties.
The deepCopy
method of course creates a new instance and returns it, detaching it from the original memory reference to make sure the original cannot be changed. This is especially important when you include mutable objects in your immutable object. In those cases, you may even need to detach the memory reference in your getter methods by doing a deepCopy and return that, instead of the property as is done in how to get and set, otherwise you may inadvertently introduce a mutable value.
When to use final #
As with most best practices, there are a lot of opinions regarding when to use the immutable pattern, but simply put:
- Use final for all properties that do not have a setter
- Use final for all classes that will not be extended
- Use final for all methods that will not be extended
- Use final for all input parameters that will not be updated in a method
- Use final for all local variables that will not be updated