ReviseAlgo Logo

Object-Oriented Programming

Immutable Classes

Analyze the recipe for immutability: final classes, final private fields, and defensive copying.

Interview: Focuses on design constraints for immutability, defensive copies, and thread safety benefits.

Last Updated: June 13, 2026 10 min read

An Immutable Class is a class whose instances cannot be modified after creation. Immutability simplifies concurrent programming by making objects inherently thread-safe.

Core Idea

Immutable classes cannot change their state after construction. This is enforced using final classes and final private fields.

Why It Matters

Mutable references in immutable classes can leak their state unless defensive copies are made during construction and retrieval.

Interview Lens

Tests the five rules of immutability, defensive copying, and thread-safety invariants.

The Recipe for Immutability

To design a class as immutable, you must follow five strict rules:

  1. Declare the class as final: Prevents subclasses from overriding methods and introducing mutable behavior.
  2. Make all fields private and final: Restricts direct access and ensures fields can only be set during constructor execution.
  3. Provide no setter methods: Prevents modifying fields after construction.
  4. Defensive Copying on Constructor: If your class references mutable fields (like arrays or collections), clone or copy the inputs during construction. This prevents external code from modifying the internal state using the original references.
  5. Defensive Copying on Getters: Return clones or unmodifiable wrappers of mutable fields instead of returning the direct reference pointers.

Common Pitfalls

  • Leaking references on construction: Storing input references directly without copying them, allowing external modifications.
  • Leaking references on retrieval: Returning direct references of internal lists or maps in getter methods.
  • Assuming final collections are immutable: Marking collection reference variables final but failing to prevent state modifications.

Best Practices

  • Follow the five-step recipe strictly when designing data objects (DTOs) or domain models.
  • Use Java Records (introduced in Java 14) to quickly declare immutable data carrier classes. Records automatically declare fields private/final and generate constructor and accessor methods.
  • Use unmodifiable collection wrappers (e.g. List.copyOf()) when performing defensive copying.

Interview-Relevant Information

Q1: Why is defensive copying necessary in the constructor of an immutable class?
Answer: If the constructor stores a reference to a mutable object directly (like a List), the caller still holds a reference to that object. The caller could modify the list outside the class, changing the immutable object's internal state. Defensive copying creates a new list copy to isolate the state.

Q2: How do Java Records support immutability?
Answer: Records are implicitly final classes. All fields declared in the record header are implicitly private and final. The compiler automatically generates constructor, accessor, equals, hashCode, and toString methods, providing a concise way to create immutable data carriers.

Quick Checklist

Can you implement the five-step recipe, write defensive copying constructors, prevent reference leaks in getters, and implement Java Records? If yes, you understand immutable classes.

Use Cases

Designing thread-safe configuration keys for concurrent caches.

Declaring immutable event payloads in distributed messaging queues.

Common Mistakes

Storing mutable list parameters directly inside class constructors without copying them first.

Returning direct references to mutable internal arrays in getter methods.