How to create immutable class in java

Viewed 3237

How can create immutable class in java. if Student class has a relationship(address) how to create immutable class. I want to make the class below immutable

  final public class Student {
        private final Address add;
            private final int sid;
            public Student(int sid, String name, Address add) {
                super();
                this.sid = sid;
                this.name = name;
                this.add = add;
            }
            private final String name;
            public int getSid() {
                return sid;
            }
            public final String getName() {
                return name;
            }
            @Override
            public String toString() {
                return "Student [add=" + add + ", name=" + name + ", sid=" + sid + "]";
            }
            public Address getAdd() {
                return add;
            }


        }

        //I want to make the class below immutable
        public class Address {
            public int getAid() {
                return aid;
            }
            public String getStreet() {
                return street;
            }
            @Override
            public String toString() {
                return "Address [aid=" + aid + ", street=" + street + "]";
            }
            int aid;
            String street;
            public Address(int aid, String street) {
                super();
                this.aid = aid;
                this.street = street;
            }

        }


        public class First {
        public static void main(String[] args) {
            Address myAdd=new Address(179,"Maihill");
            Student st=new Student(99,"anoj",myAdd);
            System.out.println(st.toString());
            myAdd.aid=2376;
            System.out.println(st);
            System.out.println("***************");
            Address pAdd=st.getAdd();
            //Here modified address instance then how we can make immutable.
                pAdd.aid=788;
            System.out.println(st);

        }
        }

Here we can modifiy address instances. Please give me idea

8 Answers

record

As of Java 16, we can use the records feature (also previewed in Java 14, and previewed in Java 15). Using record is the simplest, hassle-free way of creating Immutable class.

A record class is a shallowly immutable, transparent carrier for a fixed set of fields known as the record components that provides a state description for the record. Each component gives rise to a final field that holds the provided value and an accessor method to retrieve the value. The field name and the accessor name match the name of the component.

Let consider the example of creating an immutable rectangle

record Rectangle(double length, double width) {}

No need to declare any constructor, no need to implement equals & hashCode methods. Just any Records need a name and a state description.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

If you want to validate the value during object creation, we have to explicitly declare the constructor.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

The record's body may declare static methods, static fields, static initializers, constructors, instance methods, and nested types.

Instance Methods

record Rectangle(double length, double width) {
  
  public double area() {
    return this.length * this.width;
  }
}

static fields, methods

Since state should be part of the components we cannot add instance fields to records. But, we can add static fields and methods:

record Rectangle(double length, double width) {
  
  static double aStaticField;
 
  static void printRectanglesIntersect(Rectangle rectangleA, Rectangle rectangleB) {
    System.out.println("Checking Rectangle intersection..");
  }
}

First we need to discussed what is immutable in java.
In Java immutable means you state does not change once it has been initialize. the best example of immutable class is String.

We can also create our own immutable class, you have to do following steps.

  • Declare the Class as a final:

    Why? : As per the java final class can not be extended.
    
  • Declare all the fields as a private.

    Why? : Because private member  have not direct access out side of the class
    
  • Don't provide the setter method for that private field

    Why? : If you provide the setter method for the private members so you can access it out side of the class.    
    
  • Make all fields as final.

    Why?: As per the java final variable can be assigned only once.    
    
  • Initialize all the fields via constructor using deep copy.

                import java.util.HashMap;
        import java.util.Iterator;
    
        public final class ImmutableClassExample {
        private final int id;   
        private final String name;  
        private final HashMap<String,String> testMap;   
        public int getId() {
            return id;
        }
    
    
        public String getName() {
        return name;
        }
    
        /**
        * Accessor function for mutable objects
        */
        public HashMap<String, String> getTestMap() {
        //return testMap;
        return (HashMap<String, String>) testMap.clone();
        }
    
        /**
            * Constructor performing Deep Copy
            * @param i
            * @param n
            * @param hm
        */
    
        public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
            System.out.println("Performing Deep Copy for Object initialization");
            this.id=i;
            this.name=n;
            HashMap<String,String> tempMap=new HashMap<String,String>();
            String key;
            Iterator<String> it = hm.keySet().iterator();
            while(it.hasNext()){
                key=it.next();
                tempMap.put(key, hm.get(key));
            }
            this.testMap=tempMap;
        }
    
    
      /**
        * Constructor performing Shallow Copy
        * @param i
        * @param n
     * @param hm
     */
    /**
    public ImmutableClassExample(int i, String n, HashMap<String,String> hm){
    System.out.println("Performing Shallow Copy for Object initialization");
    this.id=i;
    this.name=n;
    this.testMap=hm;
    } 
    
    */
        /**
        * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
        * @param args
    */
    public static void main(String[] args) {
        HashMap<String, String> h1 = new HashMap<String,String>();
        h1.put("1", "first");
        h1.put("2", "second");
    
    String s = "original";
    
    int i=10;
    
    ImmutableClassExample ce = new ImmutableClassExample(i,s,h1);
    
        //Lets see whether its copy by field or reference
        System.out.println(s==ce.getName());
        System.out.println(h1 == ce.getTestMap());
        //print the ce values
        System.out.println("ce id:"+ce.getId());
        System.out.println("ce name:"+ce.getName());
        System.out.println("ce testMap:"+ce.getTestMap());
        //change the local variable values
        i=20;
        s="modified";
        h1.put("3", "third");
        //print the values again
            System.out.println("ce id after local variable change:"+ce.getId());
            System.out.println("ce name after local variable change:"+ce.getName());
            System.out.println("ce testMap after local variable change:"+ce.getTestMap());
    
            HashMap<String, String> hmTest = ce.getTestMap();
            hmTest.put("4", "new");
    
            System.out.println("ce testMap after changing variable from accessor 
            methods:"+ce.getTestMap());
    
            }
    
        }
    

you could use the lombok @Valueannotation which creates an immutable class ie. it makes all fields private and final and makes the class itself final as well. used collections are also immutable:

@Value
@Builder
public class Immutable {

    private String str;
    private int value;
    private List<String> strings;

}
Related