How to create immutable class in Java?
Immutable Object
Once created its state can not be altered.
String is good example of Immutable class in Java which you use in your day-to-day programming.Read: Why String is immutable in Java?
Most important benefit of immutable class is, It provides thread safety so you don't have to worry about its value getting changed in multi-threaded environment. Also immutable class with valid hashCode() and equals() method is good choice for Map key.
Points to be taken care while creating immutable class.
- Class must be declared as final so it can not be extended. i.e public final class ClassName.
- All variables/critical methods should be private so it can not be accessed outside of class. i.e private int x;.
- Make all mutable variables final so it can not be changed after initialization.
i.e private final int x;. - Initialize all variables via constructor only by performing deep copy. (follow example)
- Do not provide setter methods for variables. i.e public void setX(int x) {this.x = x;}
- Return cloned object/variable in getter method rather returning actual object. (follow example)
Source code (ImmutableClassLatLon.java)
import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Example of Immutable class in java. * @author javaQuery * @date 2019-12-10 * @Github: https://github.com/javaquery/Examples */ public final class ImmutableClassLatLon { private final double latitude; private final double longitude; private final List<String> labels; /** * @param latitude * @param longitude * @param labels */ public ImmutableClassLatLon(double latitude, double longitude, List<String> labels) { this.latitude = latitude; this.longitude = longitude; if(labels != null && !labels.isEmpty()){ this.labels = new ArrayList<>(labels); }else{ this.labels = null; } } /** * Will return new copy of List rather returning reference to current list. * @return */ public List<String> getLabels() { return labels != null ? new ArrayList<>(labels) : null; } /** * Get new object of ImmutableClassLatLon with updated label. * @param label * @return ImmutableClassLatLon */ public ImmutableClassLatLon addLabel(String label){ List<String> temporary = new ArrayList<>(); if(labels != null && !labels.isEmpty()){ temporary.addAll(labels); } temporary.add(label); return new ImmutableClassLatLon(latitude, longitude, temporary); } public static void main(String[] args) { ImmutableClassLatLon classLatLon = new ImmutableClassLatLon(23.0225, 72.5714, Arrays.asList("India")); System.out.println("classLatLon address: " + classLatLon); System.out.println("\n- classLatLon getLabels and add label -"); System.out.println("classLatLon labels before: " + classLatLon.getLabels()); /* classLatLon.getLabels() will return new copy of List rather returning reference to current list */ List<String> localLables = classLatLon.getLabels(); localLables.add("Hindi"); System.out.println("localLables: " + localLables); System.out.println("classLatLon labels after: " + classLatLon.getLabels()); System.out.println("\n- add new label to classLatLon -"); System.out.println("classLatLon add label before: " + classLatLon.getLabels()); /* When new label is added it will return new object of ImmutableClassLatLon rather updating current object's list */ ImmutableClassLatLon classLatLonNewLabel = classLatLon.addLabel("Asia"); System.out.println("classLatLon add label after: " + classLatLon.getLabels()); System.out.println("classLatLonNewLabel address: " + classLatLonNewLabel); System.out.println("classLatLonNewLabel labels: " + classLatLonNewLabel.getLabels()); } }Output
As you can see after initialization of ImmutableClassLatLon@36baf30c, user can not change its state even if we provided operation on it.
classLatLon address: com.javaquery.core.immutable.ImmutableClassLatLon@36baf30c - classLatLon getLabels and add label - classLatLon labels before: [India] localLables: [India, Hindi] classLatLon labels after: [India] - add new label to classLatLon - classLatLon add label before: [India] classLatLon add label after: [India] classLatLonNewLabel address: com.javaquery.core.immutable.ImmutableClassLatLon@7a81197d classLatLonNewLabel labels: [India, Asia]Further Reading
What is the difference between final and effectively final?
How HashMap works internally in Java?
How LinkedHashMap works internally in Java?
0 comments :