Breaking Immutability: Avoiding A Developer’s Mistake

We started to talk about object immutability and how we should code our classes to avoid concurrency problems.

In this post, we are going deep on how to avoid a developer’s mistake on immutability, using some interesting language features like final reserve word and immutable lists.

TRY IT YOURSELF: You can find this post source code here.

Object’s Immutability Series

A Developer’s Mistake

We created an interesting immutable class in our previous post:

class Person{
 private String name;
 private String address;
 private LocalDate birthDate;
 
 public Person(String name, String address, LocalDate birthDate){
  this.name = name;
  this.address = address;
  this.birthDate = birthDate;
 }
 
 public Person setName(String name){
   return new Person(name, this.address, this.birthDate);
 }
 
 public Person setAddress(String address){
   return new Person(this.name, address, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.address, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public String getAddress(){
   return this.address;
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

However, a new developer on the team has a new requirement, update the person name, but, he must not create a new object, so, as he likes to set good method names, he creates a new method named updateName:

NOTE: You might think: “that requirement is pretty stupid, that won’t happen”, well, remember, this is software development, everything can happen.

class Person{
 private String name;
 private String address;
 private LocalDate birthDate;
 
 public Person(String name, String address, LocalDate birthDate){
  this.name = name;
  this.address = address;
  this.birthDate = birthDate;
 }
 
 public void updateName(String name){
  this.name = name; 
 }

 public Person setName(String name){
   return new Person(name, this.address, this.birthDate);
 }
 
 public Person setAddress(String address){
   return new Person(this.name, address, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.address, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public String getAddress(){
   return this.address;
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

NOTE: Moreover, you might think: “You told us to not use setter methods, so, that developer shouldn’t create that one”, well, if you look harder, that is not a setter. Actually, that is a better name that a setter if you think.

Well, that method breaks our immutable class.

TRY IT YOURSELF: You can find this source code here.

Adding some Walls on the Developer’s Way

Let’s use some language specific features to avoid this damage:

class Person{
 private final String name;
 private final String address;
 private final LocalDate birthDate;
 
 public Person(String name, String address, LocalDate birthDate){
  this.name = name;
  this.address = address;
  this.birthDate = birthDate;
 }
 
 public void updateName(String name){
  this.name = name; 
 }

 public Person setName(String name){
   return new Person(name, this.address, this.birthDate);
 }
 
 public Person setAddress(String address){
   return new Person(this.name, address, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.address, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public String getAddress(){
   return this.address;
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

Now, Java helps us to avoid that “mistake”. The line 13 won’t compile because final properties cannot be changed after they are initialized.

NOTE: Java uses a final word, however, you can find the equivalent in any language, like val in Kotlin or readonly in .net.

TRY IT YOURSELF: You can find this source code here.

The Object Final: Getting More Troubles

Okey, Person class looks good. Now, requirements changed again, and a Person can have multiple addresses:

class Person{
 private final String name;
 private final List<String> addresses;
 private final LocalDate birthDate;
 
 public Person(String name, List<String> addresses, LocalDate birthDate){
  this.name = name;
  this.addresses = addresses;
  this.birthDate = birthDate;
 }

 public Person setName(String name){
   return new Person(name, this.addresses, this.birthDate);
 }
 
 public Person setAddress(List<String> addresses){
   return new Person(this.name, addresses, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.addresses, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public List<String> getAddresses(){
   return this.addresses;
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

Looks good, doesn’t it?. Well, there is a problem, let’s see how a client could use this class:


//Creating a mutable list
List<String> addresses = new ArrayList<>();
adresses.add("My address 1");
adresses.add("My address 2");

Person daniel = new Person("Daniel", addresses, LocalDate.of(1988, 9, 8));

//Breaking immutability
daniel.getAddresses().add("Breaking the immutability 3")

Okey, we just added a new address to the Person, it means, the Person state changed. Immutability is broken.

NOTE: Remember, an object is immutable if his whole hierarchy and associations are immutable.

CAREFUL: Any mutable object could move us to this problem, for instance, Date and Calendar are two mostly used objects, and they are mutable.

TIP: String and LocalDate are immutable. LocalDate improves the Date API we had previously to Java 8, that API was poorly designed and mutable only.

TRY IT YOURSELF: You can find this source code here.

Fixing the Object Final

Well, let’s fix it:

class Person{
 private final String name;
 private final List<String> addresses;
 private final LocalDate birthDate;
 
 public Person(String name, List<String> addresses, LocalDate birthDate){
  this.name = name;
  this.addresses = new ArrayList<>(addresses);
  this.birthDate = birthDate;
 }

 public Person setName(String name){
   return new Person(name, this.addresses, this.birthDate);
 }
 
 public Person setAddress(List<String> addresses){
   return new Person(this.name, addresses, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.address, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public List<String> getAddresses(){
   return new ArrayList<>(this.addresses);
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

Now, we created a new list in the Person constructor, or when we return it. This allows as to keep the immutability of Person class.

NOTE: If the list is huge, this could be expensive and not a good solution, due to ArrayList constructor does a full copy of the collection. So, use this carefully.

CAREFUL: See line 8? why do we need to do that? well, remember, that list we receive as parameter, was created from the client perspective, and it could be mutable, we cannot control that, so, we must guarantee the immutable features from Person class perspective.

TRY IT YOURSELF: You can find this source code here.

Improvement Performance and Readability

Let’s move to a better solution using a Java feature: UnmodifiableList.

class Person{
 private final String name;
 private final List<String> addresses;
 private final LocalDate birthDate;
 
 public Person(String name, List<String> addresses, LocalDate birthDate){
  this.name = name;
  this.addresses = Collections.unmodifiableList(addresses);
  this.birthDate = birthDate;
 }

 public Person setName(String name){
   return new Person(name, this.addresses, this.birthDate);
 }
 
 public Person setAddress(List<String> addresses){
   return new Person(this.name, addresses, this.birthDate);
 }
 
 public Person setBirthDate(LocalDate birthDate){
   return new Person(this.name, this.address, birthDate);
 }
 
 public String getName(){
   return this.name;
 }
 
 public List<String> getAddresses(){
   return this.addresses;
 }
 
 public LocalDate getBirthDate(){
   return this.birthDate;
 }
}

Now, we are creating an ImmutableList on line 8, and we remove the expensive ArrayList constructor over the other lines.

TIP: ImmutableList uses the wrapper pattern to convert a normal list into an immutable one. It doesn’t require an expensive copy or similar approach.

TRY IT YOURSELF: You can find this source code here.

Final Thought

Immutable objects are complex to build, but, solve a lot of troubles with handling state and concurrency.

Person class was small, what if our classes are bigger? and with a lot of other relations? well, the Builder pattern is a good choice.

Now, should you have unit tests about if a class is still immutable? well, yes, we are going to talk about in the following post.

If you liked this post and are interested in hearing more about my journey as a Software Engineer, you can follow me on Twitter and travel together.

Advertisement

3 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s