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