Table of Contents

Immutable objects

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

Advantages

Disadvantages

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

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) {
        this.id = id;
        this.name = 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;
    }
 
    @Override
    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);
        System.out.println(record);
 
        tokens.remove(0);
        System.out.println(record);
 
        System.out.println(record.withName("Two"));
 
        record.getTokens().add("Token 3");
        System.out.println(record);
 
    }
}

Output:

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(ImmutableCollections.java:71)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75)
	at ro.medjava.immutability.TestRecord.main(Record.java:65)

Immutable classes in JDK

Some immutable classes from java api:

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.