Java Program to Create an Immutable Class

Updated on December 20, 2024
Create an immutable class header image

Introduction

Immutable classes in Java are those which once created, their state cannot be changed. This is particularly useful when you need to ensure that an object remains constant throughout its lifecycle and achieve thread safety without synchronization. Immutable objects are foundational for functional programming style, allowing for easier maintenance and understanding of the application flow.

In this article, you will learn how to create an immutable class in Java. Explore key techniques to ensure the class cannot be modified after its instantiation, from using final fields to returning copies of mutable objects. By applying these strategies, you enhance the robustness and thread-safety of your applications.

Understanding Immutability

Basic Principles of Immutable Classes

  1. Declare the class as final to prevent subclassing.
  2. Set all fields private and final.
  3. Do not provide setters.
  4. Ensure exclusive access to any mutable fields.

Example: Creating a Basic Immutable Class

  1. Define a final class with a final data field.

  2. Provide a constructor to set the field.

    java
    public final class ImmutableValue {
        private final int value;
    
        public ImmutableValue(int value) {
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    }
    

    This code outlines a simple immutable class ImmutableValue with one field value. Since it's declared with the final keyword, you cannot change the value once it's set, nor can you inherit from this class.

Handling Complex Types

Immutable Classes with Mutable Objects

  1. Return new copies of mutable objects when necessary.
  2. Use unmodifiable wrappers for collections.

Example: Immutable Class with Mutable Fields

  1. Consider a class that includes a mutable List as a field.

  2. Make a deep copy of the list in the constructor and on the getter method.

    java
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Collections;
    
    public final class ComplexImmutable {
        private final List<String> items;
    
        public ComplexImmutable(List<String> items) {
            this.items = new ArrayList<>(items);
        }
    
        public List<String> getItems() {
            return Collections.unmodifiableList(items);
        }
    }
    

    This example demonstrates an immutable class ComplexImmutable that contains a mutable object (a List). The class makes a deep copy of the list in its constructor and provides it in a way that the internal list can't be modified from outside, thus maintaining immutability.

Testing the Immutable Class

Example: Testing Immutability

  1. Create instances of your immutable class.

  2. Attempt to modify the instances and verify if the state changes.

    java
    public class TestImmutable {
        public static void main(String[] args) {
            ImmutableValue val = new ImmutableValue(100);
            System.out.println("Value: " + val.getValue());
    
            List<String> list = new ArrayList<>();
            list.add("Hello");
            list.add("World");
            ComplexImmutable complex = new ComplexImmutable(list);
            System.out.println("Items: " + complex.getItems());
            list.add("Test");
            System.out.println("After trying to modify original list: " + complex.getItems());
        }
    }
    

    In this test code, you create an object of ImmutableValue and try modifying ComplexImmutable by changing the original list. Notice how the output reveals that ComplexImmutable's content remains unchanged, signifying true immutability.

Conclusion

Crafting immutable classes in Java provides great benefits in ensuring data consistency and simplifying concurrency control in applications. By adhering to principles such as final class declarations, final fields, and careful handling of mutable objects, you equip your programs with definitively unchangeable objects. Expanding this pattern will streamline your system's architecture, paving the way for more maintainable and error-resistant code. Implement these strategies in your next project to harness the power of immutability.