为了防止类的约束条件受到来自类外部的破坏,在进行类设计时需要进行适当的保护性拷贝。下面举一个例子,设计了一个不可变的类Period:
public final class Period{
private final Date start;
private final Date end;
public Period(Date start, Date end){
if (start.compareTo (end)>0){
throw new Illegal ArgumentException(start + " after”+end);
}
this.start = start;
this.end = end;
}
public Date getStart() {
return start;
}
public Date getEnd() {
return end;
}
}
Period类和其成员变量由final修饰,看上去不可变。但是如果在外部按照下面代码调用这个类,
Date start = new Date ();
Date end = new Date() ;
Period period = new Period ( start, end) ;
end.setYear(78);
其成员变量end仍然被改变,简单的拷贝并不能保证不可变,因此我们不得不将Period类的构造器作如下修改:
public Period(Date start, Date end){
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (start.compareTo (end)>0){
throw new Illegal ArgumentException(start + " after”+end);
}
}
进行保护性拷贝后便无法在类的外部再修改成员变量的值。值得注意的是,将时间校验放在有效性拷贝之后可以防止在高并发情况下,参数在校验之后拷贝之前被修改。同时我们还得注意set函数和get,set函数的情况和上述构造器类似,同样修改即可。为了防止外部对象从get函数获得成员对象的实例,以getStart()为例将get函数修改为:
public Date getStart(){
return new Date(start.getTime());
}
修改构造器和访问函数后,类Period真正成为不可变的类。
在编写代码的时候无论是向客户端提供的对象还是接受客户端的对象,又要考虑自身对象的安全性,防止发生不可预期的后果。例如,在使用Set和Map对象的时候,将对象作为key值时,就必须考虑到key被修改的情况。