Java and Immutability Part II

In part I, immutibility was defined. However, all the examples used very simple objects and variables. In this part, I make my first attempt to create a user defined class that is immutable. And... I failed miserably. :) Here is the source code.

FlawedEmployee.java (Click to get code text)

   1 import java.util.Date;
   2 import java.util.Calendar;
   3 import java.io.PrintWriter;
   4 
   5 public final class FlawedEmployee{
   6   
   7   private final String name;
   8   private final int id;
   9   private final Calendar hireDate;
  10   
  11   private FlawedEmployee(String name, int id, Calendar hireDate){
  12     this.name = name;
  13     this.id = id;
  14     this.hireDate = hireDate;
  15   }
  16   
  17   public static FlawedEmployee getEmployeeInstance(String name, int id, Calendar cal){
  18     return new FlawedEmployee(name, id, cal);
  19   }
  20   
  21   public String getName(){
  22     return this.name;
  23   }
  24   
  25   public int getId(){
  26     return this.id;
  27   }
  28   
  29   public Calendar getHireDate(){
  30     return this.hireDate;
  31   }
  32   
  33   public String printEmployee(){
  34     return "Name: " + this.getName() + "\nID: " + this.getId() + "\nHire Date:
 " + this.getHireDate().getTime() + "\n";
  35     
  36   }
  37   
  38   public static void main(String[] args){
  39     PrintWriter pw = new PrintWriter(System.out, true);
  40     Calendar startCal = Calendar.getInstance();
  41     
  42     FlawedEmployee joe = FlawedEmployee.getEmployeeInstance("Joe", 1, startCal);
  43     
  44     pw.println("\n== Employee Start ==");
  45     pw.println(joe.printEmployee());
  46 
  47     pw.println("\n== Change startCal ==");
  48     startCal.set(2011, 4, 31, 12, 00);
  49     pw.println(joe.printEmployee());
  50     
  51     pw.println("\n== Changed Retreived Date ==");
  52     Calendar mainCal = joe.getHireDate();
  53     mainCal.set(2010, 11, 29, 12, 00);
  54     pw.println(joe.printEmployee());
  55     
  56   }
  57 }

As the class name may suggest, this is an attempt to model a read only employee object. However, the design is flawed for several reasons as demonstrated in the main method. Here is the output when the program is run.

== Employee Start ==
Name: Joe
ID: 1
Hire Date: Sun Jan 30 11:52:11 PST 2011

== Change startCal ==
Name: Joe
ID: 1
Hire Date: Tue May 31 12:00:11 PDT 2011

== Changed Retreived Date ==
Name: Joe
ID: 1
Hire Date: Wed Dec 29 12:00:11 PST 2010

On lines 39-45, all objects are initialized and the contents of a FlawedEmployee object is printed. Everything works as expected.

Things get interesting on lines 47-49. The startCal object is changed. Now, when the FlawedEmployee is printed, it shows the updated value instead of the original date. Since the hireDate property is a reference variable, other references to that object change the value in the joe object.

Lines 51 - 54 shows that at any time I could get a reference to hireDate and change the contents of that property.

So obviously, some modifications need to be made to make this object truely read only. These fixes are address in ImmutableEmployee.java.

ImmutableEmployee.java (Click to get code text)

   1 import java.util.Date;
   2 import java.util.Calendar;
   3 import java.io.PrintWriter;
   4 
   5 public final class ImmutableEmployee{
   6   
   7   private final String name;
   8   private final int id;
   9   private final Calendar hireDate;
  10   
  11   private ImmutableEmployee(String name, int id, Calendar hireDate){
  12     this.name = name;
  13     this.id = id;
  14     this.hireDate = hireDate;
  15   }
  16   
  17   public static ImmutableEmployee getEmployeeInstance(String name, int id, Calendar cal){
  18     Calendar calCopy = Calendar.getInstance();
  19     calCopy.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 
cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR), cal.get(Calendar.MINUTE)); 20 return new ImmutableEmployee(name, id, calCopy); 21 } 22 23 public String getName(){ 24 return this.name; 25 } 26 27 public int getId(){ 28 return this.id; 29 } 30 31 public Calendar getHireDate(){ 32 Calendar calCopy = Calendar.getInstance(); 33 calCopy.set(this.hireDate.get(Calendar.YEAR), this.hireDate.get(Calendar.MONTH),
this.hireDate.get(Calendar.DAY_OF_MONTH), this.hireDate.get(Calendar.HOUR), this.hireDate.get(Calendar.MINUTE)); 34 return calCopy; 35 } 36 37 public String printEmployee(){ 38 return "Name: " + this.getName() + "\nID: " + this.getId() + "\nHire Date: " + this.getHireDate().getTime() + "\n"; 39 40 } 41 42 public static void main(String[] args){ 43 PrintWriter pw = new PrintWriter(System.out, true); 44 Calendar startCal = Calendar.getInstance(); 45 46 ImmutableEmployee joe = ImmutableEmployee.getEmployeeInstance("Joe", 1, startCal); 47 48 pw.println("\n== Employee Start =="); 49 pw.println(joe.printEmployee()); 50 51 pw.println("\n== Change startCal =="); 52 startCal.set(2011, 4, 31, 12, 00); 53 pw.println(joe.printEmployee()); 54 55 pw.println("\n== Changed Retreived Date =="); 56 Calendar mainCal = joe.getHireDate(); 57 mainCal.set(2010, 11, 29, 12, 00); 58 pw.println(joe.printEmployee()); 59 60 } 61 }

Now when the main method is run, the output is a little different.

== Employee Start ==
Name: Joe
ID: 1
Hire Date: Sun Jan 30 00:14:58 PST 2011

== Change startCal ==
Name: Joe
ID: 1
Hire Date: Sun Jan 30 00:14:58 PST 2011

== Changed Retreived Date ==
Name: Joe
ID: 1
Hire Date: Sun Jan 30 00:14:58 PST 2011

Notice that the output doesn't change. That is because the new class protects the contents of hireDate.

On lines 17-21, when the Calendar object is set, a copy is made thus creating a new reference variable to the same data. Now changes made to the original Calendar object have no effect on the joe object.

Lines 31-35 demonstrates how the Calendar object is protected when a call is made to get Calendar data. Instead of giving out a reference to the hireDate property, a copy is made and that new reference is returned. In both cases, the reference to the Calendar object is protected.

To protect a reference variables, copies must be used to protect the contents of the object. Now I should have an immutable employee object that is thread safe and immutable. Using this approach should work for any object that needs to be made immutable.

References

There a couple of sources that really make this how-to possible.