DEV Community

Garvit Khamesra
Garvit Khamesra

Posted on • Edited on

How to create an Immutable Class in Java?

Immutable Classes

Immutable classes are those whose object once created will not change its value. All wrapper classes in Java are Immutable classes. For eg. String, BigInteger, Byte, etc.

Immutable classes are good for caching because you don’t have to worry about the value changes.

Another benefit of the immutable class is that it is inherently thread-safe.

How to Create an immutable class in Java?

To create an immutable class in Java, we need to make sure that the class we create by any mechanism should above to provide a way to change its object's values. To do that we can follow few rules, which help us to prevent that behavior.

  • Declare the class as final so it cannot be extended. If the class cannot be extended the child cannot make any changes.
  • All class members should be private so they cannot be accessed outside of class.
  • The class should not contain any setter methods to change the value of class members.
  • The getter method should return the copy of class members.
  • Variables are initialized only by using the constructor.
  • Perform cloning or deep copy of objects in the getter methods to return a copy rather than returning the actual object reference.

Example 1:

import java.util.HashMap;
import java.util.Map;

// class is declared final
final class Immutable {

    // private class members
    private final String name;
    private final int date;
    private final Map<String, String> metadata;

    public Immutable(String name, int date, Map<String, String> metadata) {
        // class members are initialized using constructor
        this.name = name;
        this.date = date;

        Map<String, String> tempMap = new HashMap<>();
        for (Map.Entry<String, String> entry : metadata.entrySet()) {
            tempMap.put(entry.getKey(), entry.getValue());
        }
        this.metadata = tempMap;
    }

    // getter method returns the copy of class members
    public String getName() {
        return name;
    }

    public int getDate() {
        return date;
    }

    public Map<String, String> getMetadata() {
        // Creating Map with HashMap reference
        Map<String, String> tempMap = new HashMap<>();
        for (Map.Entry<String, String> entry :
                this.metadata.entrySet()) {
            tempMap.put(entry.getKey(), entry.getValue());
        }
        return tempMap;
    }
}

Enter fullscreen mode Exit fullscreen mode

Example 2:

final class Immutable {
    private final Mutable mutableClass;
    public Immutable(Mutable mutableClass) {
        super();
        this.mutableClass = mutableClass;
    }

    public Mutable getMutableClass() throws CloneNotSupportedException {
        return (Mutable) mutableClass.clone();
    }
}

class Mutable implements Cloneable {
    public String variable;

    public Mutable(String variable) {
        super();
        this.variable = variable;
    }

    public String getVariable() {
        return variable;
    }

    public void setVariable(String variable) {
        this.variable = variable;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Mutable{" +
                "variable='" + variable + '\'' +
                '}';
    }
}

public class MainClass {
    public static void main(String[] args) throws Exception {
        Immutable immutable = new Immutable(new Mutable("Some String"));
        Mutable mutable = immutable.getMutableClass();
        System.out.println(mutable);

        mutable.setVariable("Updated String");
        System.out.println(immutable.getMutableClass());
    }
}

Enter fullscreen mode Exit fullscreen mode

#HappyCoding

Top comments (0)