Strategy for Defining Immutable Objects

A Strategy for Defining Immutable Objects:

The following rules define a simple strategy for creating immutable objects. Not all java classes documented as "immutable" follow these rules. This does not necessarily mean the creators of these classes were sloppy — they may have good reason for believing that instances of their classes never change after construction. However, such strategies require sophisticated analysis and are not for beginners.

  1. Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
  2. Make all fields final and private.
  3. Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. Even a more sophisticated approach is to make the constructor private and construct instances in factory methods.
  4. If the instance fields include references to mutable objects, don't allow those objects to be changed:
  • Don't provide methods that modify the mutable objects.
  • Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
For example: If we have a Student class which holds a reference of Address class as following

class Student{

private String firstName;
private String lastName;
private int studentId;
private Address address;

   public Student(String firstName, String lastName, int id){
          this.firstName = firstName;
          this.lastName = lastName;
          this.studentId = id;
   }

   public String getFirstName(){
         return firstName;
   }

  public void setFirstName(String fName){
           firstName = fName;
   }

   public String getLastName(){
         return lastName;
   }

  public void setLastName(String lName){
           lastName = lName;
   }

   public int getId(){
        return studentId;
   }

   public void setId(int id){
         studentId = id;
   }

  public Address getAddress(){
         return address;
   }

   public void setAddress(Address address){
          this.address = address;
   }
}

A simple address class can be written in following way.

class Address {
 private int Id;
 private String address;

 public Address(int studId, String address){
          this.id = studId;
          this.address = address;
  }

  public int getId(){
         return id;
  }

   public void setId(int id){
         this.id = id;
  }

   public String getAddress(){
         return address;
   }

   public void setAddress(String address){
          this.address = address;
   }
}

Now we can make the Student class immutable by doing following changes:

final class ImmutableStudent{

private final String firstName;
private final String lastName;
private final int studentId;
private String address;

   private ImmutableStudent(String firstName, String lastName, int id, String address){
          this.firstName = firstName;
          this.lastName = lastName;
          this.studentId = id;
          this.address = address;
   }

   public static ImmutableStudent createStudent(String fName, String lName, int id, String address){
             return new ImmutableStudent(fName, lName, id, address);
   }

   public String getFirstName(){
         return firstName;
   }

   public String getLastName(){
         return lastName;
   }

   public int getId(){
        return studentId;
   }

   public Address getAddress(){
         return new Address(studentId, address);
   }

}

instead of passing String address in createStudent factory method we can pass a copy of address
as following;

1. Change private String address; to private String Address;// class
2. Change the method as following

 public static ImmutableStudent createStudent(String firstName, String lastName, int id, Address address){
            // Address address =(Address) address.clone(); // if clone not supported then
            Address address =  new Address(studentId, address.getAddress());
             return new ImmutableStudent(firstName, lastName, id, address);
   }

3. Change the  getAddress() as following

  public Address getAddress(){
         return new Address(studentId, address.getAddress());
         // or can be a clone if cone supported,  return (Address)address.clone();
   }

It will be a good idea to change the address class as immutable Address class to avoid cloning or creating new copy of Address Object in ImmutableStudent class.

final class Address {
 private final int Id;
 private final String address;

 public Address(int studId, String address){
          this.id = studId;
          this.address = address;
  }

  public int getId(){
         return id;
  }

  public String getAddress(){
         return address;
   }

}

Now we can remove the factory method from ImmutableStudent class and can pass address reference in constructor by making constructor public as shown here

final class ImmutableStudent {
private final String firstName;
private final String lastName;
private final int studentId;
private final Address address;

 public ImmutableStudent(String firstName, String lastName, int id, Address address){
          this.firstName = firstName;
          this.lastName = lastName;
          this.studentId = id;
          this.address = address;
   }

  public Address getAddress(){
         return address;
   }
 
  public String getFirstName(){
         return firstName;
   }

   public String getLastName(){
         return lastName;
   }

   public int getId(){
        return studentId;
   }
}

No comments :

Post a Comment