Lesson 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.
Quick check: 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.