How to make Date in Java Immutable.

Now we have a template for creating immutable objects.
  • Make all fields private
  • Don't provide mutators - setter ()
  • Ensure that methods can't be overridden by either making the class final (Strong Immutability) or making your methods final (Weak Immutability)
  • If a field isn't primitive or immutable, make a deep clone on the way in and the way out.

Protect mutable fields

The last requirement which many people fall victim too, is to build your immutable class from primitive types or immutable fields, otherwise you have to protect mutable fields from manipulation. To highlight this problem, we'll use the example of a supposedly immutable class representing a person. Our class has a first and last name, as well as a date of birth.
 import java.util.Date;
 public final class BrokenPerson
 {
  private String firstName;
  private String lastName;
  private Date dob;

  public BrokenPerson( String firstName,
    String lastName, Date dob)
  {
   this.firstName = firstName;
   this.lastName = lastName;
   this.dob = dob;
  }

  public String getFirstName()
  {
   return this.firstName;
  }
  public String getLastName()
  {
   return this.lastName;
  }
  public Date getDOB()
  {
   return this.dob;
  }
 }
This all looks fine, until someone uses it like this:
 Date myDate = new Date();
 BrokenPerson myPerson =
   new BrokenPerson( "David", "O'Meara", myDate );
 System.out.println( myPerson.getDOB() );
 myDate.setMonth( myDate.getMonth() + 1 );
 System.out.println( myPerson.getDOB() );
Depending on the dates entered, the output could be something like this:
 Mon Mar 24 21:34:16 GMT+08:00 2003
 Thu Apr 24 21:34:16 GMT+08:00 2003
The Date object is mutable, and the myPerson variable is referencing the same instance of the Date object as the myDate variable. When myDate changes the instance it is referencing, the myPerson instance changes too. It isn't immutable! We can defend against this by taking a copy of the of the Date instance when it is passed in rather than trusting the reference to the instance we are given.
 import java.util.Date;
 public final class BetterPerson
 {
  private String firstName;
  private String lastName;
  private Date dob;

  public BetterPerson( String firstName,
    String lastName, Date dob)
  {
   this.firstName = firstName;
   this.lastName = lastName;
   this.dob = new Date( dob.getTime() );
  }
  //etc...
Now we're close, but we're still not quite there. Our class is still open to abuse.
 BetterPerson myPerson =
   new BetterPerson( "David", "O'Meara", new Date() );
 System.out.println( myPerson.getDOB() );
 Date myDate = myPerson.getDOB();
 myDate.setMonth( myDate.getMonth() + 1 );
 System.out.println( myPerson.getDOB() );
We see here that taking a copy on the way in wasn't enough; we also need to prevent anyone from getting a reference to our mutable Date field when we pass it out.
 public Date getDOB()
 {
  return new Date( this.dob.getTime() );
 }

Make deep copies of mutable data

The only point to add is that when you copy the instance on the way in and the way out, you need to make a deep copy. Otherwise you run the risk of leaving some mutable data in your immutable class! If you are confused about the need to provide a deep copy, keep in mind that a single piece of shared mutable data, no matter how deep it is buried inside an object, makes your class mutable. When you create a copy of an object to defend against the value changing, you need to make sure your copy doesn't include this shared mutable class. You need to copy any mutable objects all the way down to the last field, and copy any nested fields until you have a completely new copy of your own. It's the only way to be safe!

0 comments: