Objects, Classes & OOP Design
Immutability & Class Design
Immutable objects are simple, safe to share, and inherently thread-safe. Minimize mutability by default: final fields, no setters, defensive copies at the boundaries — and reach for records when the class is pure data.
- Immutable = no state change after construction:
finalclass,finalfields, no setters - Immutable objects need no synchronization — share them freely across threads
- Defensive-copy mutable inputs and outputs (EJ 50)
- Classes should be either designed for inheritance or
final(EJ 19) - Minimize accessibility of everything (EJ 15)
- The cost: a new object per "change" — usually negligible, occasionally not
public final class Money {
private final BigDecimal amount;
private final Currency currency;
public Money(BigDecimal amount, Currency currency) {
this.amount = Objects.requireNonNull(amount);
this.currency = Objects.requireNonNull(currency);
}
public Money plus(Money other) {
requireSameCurrency(other);
return new Money(amount.add(other.amount), currency); // return NEW object
}
}Why it pays: an immutable object has exactly one state, established by its constructor — no temporal reasoning, no aliasing surprises, no locks. It is the cheapest form of Thread Safety there is (JCiP: "immutable objects are always thread-safe"), and safe publication comes free via final fields (Sharing Objects).
When true immutability is impractical, minimize mutability: make every field final unless it must change, keep mutable state private, and provide the smallest possible mutation surface. A class with one mutable field is vastly easier to reason about than a JavaBean with ten setters.