User Tools

Site Tools


Immutable objects

An immutable object cannot be modified after it was created. Instead, a copy of the object is returned.


  • Thread safety: they cannot change its state, so they cannot be corrupted by thread interference or observed in an inconsistent state.
  • Atomicity of failure: if an immutable object throws an exception, it's never left in an undesirable or indeterminate state.
  • Predictability: objects won't change due to coding mistakes or by 3rd party libraries. As long as we reference a data structure, we know it is the same as at the time of its creation.
  • Validity: is not needed to be tested again and again. Once we create the immutable object and test its validity once, we know that it will be valid indefinitely.


Immutable classes require a separate object for each distinct value. Creating these objects can be costly, especially if they are large.

How to implement an immutable object

  • make fields private and final
  • methods that alters the object state should return new objects
  • make copies of caller provided data (except primitives or immutable objects)
  • ensure the class cannot be overridden (make the class final, or use static factories and keep constructors private)
  • don't provide setter methods for variables.

Example of immutable class

import java.util.ArrayList;
import java.util.List;
final class Record {
    private final long id;
    private final String name;
    private final List<String> tokens;
    public Record(long id, String name, List<String> tokens) { = id; = name;
        /*  This works, but:
         - it will always duplicate elements
         - the same copy should be used also in getTokens() method.
        //this.tokens = new ArrayList<>(tokens);
        /*  If tokens is already unmodifiable (for ex. was formed using List.of),
            then it will not duplicate elements.
            Also, it will prevent the tokens to be modified
            (it will throw java.lang.UnsupportedOperationException).
        this.tokens = List.copyOf(tokens);
    public long getId() {
        return id;
    public String getName() {
        return name;
     * No simple setters, return another copy of the object.
     * @param name
     * @return Record
    public Record withName(String name) {
        return new Record(id, name, tokens);
    public List<String> getTokens() {
        //return new ArrayList<>(tokens);
        return tokens;
    public String toString() {
        return "Record (id = " + id + ", name='" + name + '\'' + ", tokens=" + tokens + ")";
class TestRecord {
    public static void main(String[] args) {
        List<String> tokens = new ArrayList<>();
        tokens.add("Token 1");
        tokens.add("Token 2");
        Record record = new Record(1, "One", tokens);
        record.getTokens().add("Token 3");


Record (id = 1, name='One', tokens=[Token 1, Token 2])
Record (id = 1, name='One', tokens=[Token 1, Token 2])
Record (id = 1, name='Two', tokens=[Token 1, Token 2])
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(
	at ro.medjava.immutability.TestRecord.main(

Immutable classes in JDK

Some immutable classes from java api:

  • java.lang.String
  • java.lang.Integer
  • java.util.Locale
  • java.awt.Font
Immutable classes can also be created using builder pattern. Builder Pattern is a better option if the immutable class has a lot of attributes and some of them are optional.
java/immutable-objects.txt · Last modified: 2023/04/08 02:38 by odefta