Answer
To create an immutable class in Java, you need to ensure that the class's state cannot be modified after its object is created. An immutable class is thread-safe and useful for scenarios requiring constant, unchangeable data, like String or Integer. Below is a step-by-step guide to creating an immutable class:
Steps to Create an Immutable Class in Java
- Declare the Class as final:
- Use the final keyword to prevent the class from being subclassed, ensuring its immutability cannot be overridden.
- Example: final class MyImmutableClass
- Make All Fields private and final:
- Declare instance variables as private to restrict direct access and final to ensure they are assigned only once (in the constructor).
- Example: private final int value;
- Provide Only Getter Methods:
- Include getter methods to access field values but avoid setters to prevent modification.
- Example: public int getValue() { return value; }
- Initialize Fields via Constructor:
- Use a constructor to set field values during object creation. Ensure no other methods can modify these fields.
- Example: public MyImmutableClass(int value) { this.value = value; }
- Handle Mutable Objects Defensively:
- If the class contains mutable fields (e.g., List, Date), return deep copies in getters and accept copies in the constructor to prevent external modification.
- Example: For a List, return Collections.unmodifiableList(new ArrayList<>(myList)).
- Ensure No Methods Modify State:
- Avoid any methods that alter field values after construction. All operations should return new objects if changes are needed.
- Example: Instead of modifying a field, return a new instance with updated values.
Example of an Immutable Class in Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
final class MyImmutableClass {
private final int value;
private final String name;
private final List<String> items;
// Constructor to initialize fields
public MyImmutableClass(int value, String name, List<String> items) {
this.value = value;
this.name = name;
// Create a defensive copy of mutable object
this.items = new ArrayList<>(items);
}
// Getter for value
public int getValue() {
return value;
}
// Getter for name
public String getName() {
return name;
}
// Getter for list (return unmodifiable copy)
public List<String> getItems() {
return Collections.unmodifiableList(items);
}
}
Testing the Immutable Class
public class Main {
public static void main(String[] args) {
List<String> inputList = new ArrayList<>();
inputList.add("Item1");
MyImmutableClass obj = new MyImmutableClass(42, "Test", inputList);
// Access values
System.out.println(obj.getValue()); // Output: 42
System.out.println(obj.getName()); // Output: Test
System.out.println(obj.getItems()); // Output: [Item1]
// Try modifying the input list
inputList.add("Item2");
System.out.println(obj.getItems()); // Still [Item1] (unchanged)
// Try modifying returned list (will throw exception)
try {
obj.getItems().add("Item3");
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify immutable list");
}
}
}
Key Points for Immutability
- Thread Safety: Immutable objects are inherently thread-safe, as their state cannot change.
- Use Cases: Ideal for caching, configuration objects, or sharing data across threads.
- Built-in Examples: Java’s String, Integer, and LocalDate are immutable.
- Defensive Copying: Always copy mutable objects in constructors and getters to prevent external changes.
Summary
To create an immutable class in Java, make the class final, use private final fields, provide only getters, initialize fields via constructor, and handle mutable objects with defensive copying. This ensures the object’s state remains constant after creation, as shown in the example above.