पाठ 5 / 25

Immutable Objects

Design immutable classes and understand defensive copying.

Objects that never change

An immutable object cannot change state after construction. Java's String, Integer and LocalDate are immutable; operations such as toUpperCase() return new objects. Immutability brings big benefits: objects are automatically thread-safe, can be shared and cached freely, make safe map keys, and are easier to reason about because no other code can change them behind your back. To make a class immutable in Java: make all fields private final; provide no setters; make the class final (or its constructor private) so subclasses cannot add mutability; and use defensive copies for mutable inputs and outputs such as lists and arrays, otherwise a caller who still holds the original list can change your object's "immutable" contents. Records (Java 16+) make simple immutable data carriers concise. Methods that would "change" an immutable object return a modified copy instead, sometimes called withers (withPrice(...)).

An immutable class with defensive copies

Callers cannot modify the internal list, either through the constructor argument or the getter.

import java.util.List;

public final class Order {
    private final String id;
    private final List<String> items;

    public Order(String id, List<String> items) {
        this.id = id;
        this.items = List.copyOf(items);       // defensive copy, and unmodifiable
    }

    public String id() { return id; }
    public List<String> items() { return items; }   // already unmodifiable, safe to return

    public Order withItem(String item) {           // "modify" by returning a new object
        var copy = new java.util.ArrayList<>(items);
        copy.add(item);
        return new Order(id, copy);
    }
}

// Java 16+ record: final fields, constructor, accessors, equals/hashCode/toString
public record Money(long paise, String currency) { }

A printed certificate

A printed certificate does not change; if a detail is wrong, a new certificate is issued. Anyone can hold a copy without worrying that someone else will edit theirs.

त्वरित जाँच: Why does an immutable class make defensive copies of mutable constructor arguments such as lists?

  • To save memory
  • So the caller cannot change the object's state later through the list it still holds
  • Because Java requires it
  • To make the class faster
Answer

So the caller cannot change the object's state later through the list it still holds — Without a copy, the caller's reference to the same list could modify the object.