Close

Create Immutable Objects

[Last Updated: Dec 7, 2016]

Java 

To make an object immutable we have to follow these requirements while creating the corresponding class:

  • All instance/members field should be final and private. This will force initialization of member fields via constructor or during declaration only. This will also disallow to create setters which can change the member fields.
  • Make the methods final: if not final they can be freely overridden and mutable properties can be added.
    More strictly make the class final itself.

final public class MyData {
private final String aStr;
private final String anInt;

public MyData (String aStr, String anInt) {
this.aStr = aStr;
this.anInt = anInt;
}

public String getAStr () {
return aStr;
}

public String getAnInt () {
return anInt;
}
}


if the instance fields include references to mutable objects, don't allow those objects to be changed:

  • Don't provide methods that modify the mutable objects.
  • Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods

For example a mutable collection should only be returned as a copy:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class MyData {
private final List<String> list = new ArrayList<>();

public MyData (String... aStr) {
Arrays.stream(aStr).forEach(list::add);
}

public List<String> getList () {
//return the copy
return new ArrayList<>(list);
}
}


Copy the external mutable collection which is passed in the constructor:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class MyData {
private final List<String> list;

public MyData (List<String> list) {
this.list = new ArrayList(list);
}
....
}


Don't let pass mutable objects. We can do that by using type annotations:

public final class MyData {
private final ExternalObject externalObject;

public MyData (@Immutable ExternalObject externalObject) {
this.externalObject = externalObject;
}
}

Note frameworks like Checker framework provides compile time type checking based on type annotations.


In case of generics:

public final class MyData<@Immutable T> {
private final T externalObject;

public MyData (T externalObject) {
this.externalObject = externalObject;
}
}



See Also