编写不变类很容易。如果以下几点都为真,那么类就是不变的:
- 它的所有字段都是 final
- 该类声明为 final
- 不允许
this
引用在构造期间转义 - 任何包含对可变对象(如数组、集合或类似
Date
的可变类)引用的字段:- 是私有的
- 从不被返回,也不以其它方式公开给调用程序
- 是对它们所引用对象的唯一引用
- 构造后不会更改被引用对象的状态
Guidelines for writing immutable classes
Writing immutable classes is easy. A class will be immutable if all of the following are true:
- All of its fields are final
- The class is declared final
- The
this
reference is not allowed to escape during construction - Any fields that contain references to mutable objects, such as arrays, collections, or mutable classes like
Date
:- Are private
- Are never returned or otherwise exposed to callers
- Are the only reference to the objects that they reference
- Do not change the state of the referenced objects after construction
这儿是一个不变类文件示例:
public final class ImmutableArrayHolder {
private final int[] theArray;
// Right way to write a constructor -- copy the array
public ImmutableArrayHolder(int[] anArray) {
this.theArray = (int[]) anArray.clone();
}
// Right way to write an accessor -- don't expose the array reference
public int getArrayLength() {
return theArray.length;
}
public int getArray(int n) {
return theArray[n];
}
// Right way to write an accessor -- use clone()
public int[] getArray() {
return (int[]) theArray.clone();
}
}
下面是对它的一个测试类:import junit.framework.*;
public class ImmutableArrayHolderTest extends TestCase {
private ImmutableArrayHolder iah;
private int[] array = {1, 2, 3, 4, 5};
protected void setUp() {
iah = new ImmutableArrayHolder(array);
}
public static TestSuite suite() {
return new TestSuite(ImmutableArrayHolderTest.class);
}
public void testEquals() {
array[3] = 5;
Assert.assertEquals((iah.getArray())[3], 5);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
}
测试结果:
junit.framework.AssertionFailedError: expected:<4> but was:<5>
at ImmutableArrayHolderTest.testEquals(ImmutableArrayHolderTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.junit2.JUnitStarter.main(Unknown Source)
通过测试证明:array通过clone方法进行了深复制,这个类是一个不变类。