Item 39 - Make defensive copies when needed

From Effective Java 2/e by Joshua Bloch

You must program defensively, with the assumption that clients of your class will do their best to destroy its invariants.

// Broken "immutable" time period class
public final class Period {
   private final Date start;
   private final Date end;

   /**
    * @param start the beginning of the period
    * @param end the end of the period; must not precede start * @throws IllegalArgumentException if start is after end
    * @throws NullPointerException if start or end is null
    */
   public Period(Date start, Date end) {
      if (start.compareTo(end) > 0)
         throw new IllegalArgumentException(start + " after " + end);
      this.start = start;
      this.end   = end;
   }

   public Date start() { return start; }
   public Date end() { return end; }
   ...  // Remainder omitted
}

Attack the internals of a Period instance

It is essential to make a defensive copy of each mutable parameter to the constructor

// Attack the internals of a Period instance
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end); end.setYear(78); // Modifies internals of p!

// Repaired constructor - makes defensive copies of parameters
public Period(Date start, Date end) {
   this.start = new Date(start.getTime());
   this.end   = new Date(end.getTime());
   if (this.start.compareTo(this.end) > 0)
      throw new IllegalArgumentException(start +" after "+ end);
}
  • defensive copies are made before checking the validity of the parameters (Item 38), and the validity check is performed on the copies rather than on the originals*

Second attack on the internals of a Period instance

Modify the accessors to return defensive copies of mutable internal fields

// Second attack on the internals of a Period instance
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end); p.end().setYear(78); // Modifies internals of p!

// Repaired accessors - make defensive copies of internal fields
public Date start() {
   return new Date(start.getTime());
}
public Date end() {
   return new Date(end.getTime());
}
  • Do not use the clone method to make a defensive copy of a parameter whose type is subclassable by untrusted parties
  • Experienced programmers often use the primitive long returned by Date.getTime() as an internal time representation instead of using a Date reference

Posted by The Finest Artist