Immutable Classes

Why Immutability

  • Thread-safe for free: no thread can change the object, so no locks needed.
  • Safe map keys and set elements (hash never changes).
  • Easier to reason about: no "who changed this?" bugs; cacheable and shareable.
  • Examples in the JDK: String, Integer, LocalDate, BigDecimal, List.of(...).

Rules for an Immutable Class

  1. Make the class final (or use a record) so subclasses cannot add mutable state.
  2. Make all fields private final.
  3. No setters. "Changing" methods return a new object (withX(...)).
  4. Make defensive copies of mutable inputs and outputs (lists, arrays, Date).

Example

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        Price p1 = new Price(new BigDecimal("999.00"), "INR");
        Price p2 = p1.withDiscountPercent(10);
        System.out.println("original:   " + p1);
        System.out.println("discounted: " + p2);

        String s = "java";
        s.toUpperCase();                  // returns a new String, s is unchanged
        System.out.println(s);
    }
}

final class Price {
    private final BigDecimal amount;
    private final String currency;

    Price(BigDecimal amount, String currency) { this.amount = amount; this.currency = currency; }

    Price withDiscountPercent(int percent) {
        BigDecimal factor = BigDecimal.valueOf(100 - percent).divide(BigDecimal.valueOf(100));
        return new Price(amount.multiply(factor).setScale(2), currency);
    }

    @Override public String toString() { return currency + " " + amount; }
}

Output

original:   INR 999.00
discounted: INR 899.10
java