private static final Thing[] VALUES[] = {
...};
public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
或者,添加一个公有方法来返回该数组的一个备份
public static final Thing[] values(){
return VALUES.clone();
}
考虑不可变类Point拥有两个域x和y,需要得到新的Point(x+deltaX,y+deltaY),则应该这样处理:
pulibc Point add(float deltaX,float deltaY){
return new Point(this.x+deltaX,this.y+deltaY);
}
频繁重用:对一些经常用到的值,提供公有的static final常量,比如:
public static final Point PointZero = new Point(0.0,0.0)
不需要,也不应该为不可变类提供clone方法,或拷贝构造器。
有些类不能做出不可变的,仍然应该尽可能的限制他的可变性。
除非有足够的理由使域变成非final的,否则,每个域都应该是final。
不要在构造器和静态工厂之外,再额外的提供初始化方法,除非有令人信服的理由必须这么做。
继承打破了封装性,子类依赖于父类中特定功能的实现细节。
考虑下面的InstrumentedHashSet类,扩展了HashSet,用addCount来记录已添加的元素数量。
import java.util.*;
public class InstrumentedHashSet extends HashSet {
// The number of attempted element insertions
private int addCount = 0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
public static void main(String[] args) {
InstrumentedHashSet s =
new InstrumentedHashSet();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
s中的确只添加了3个元素,但s.getAddCount()返回的值是6。
期望的调用过程是这样的:
s.addAll(e1,e2,e3) ->
[
addCount+=3;
s.super.addAll(e1,e2,e3) ->
[
s.super.add(e);
] * loop(3)
]
但因为子类覆盖了父类的add(e)方法,由于多态机制,会调用子类的实现。
于是实际的调用过程是这样的:
s.addAll(e1,e2,e3,....) ->
[
addCount+=3;
[
s.add(e1,e2,e3) ->
[
addCount ++;
s.super.add(e);
]
] * loop(3)
]
如果子类不覆盖addAll方法,就可以修正这个类。
然而,这样的类虽然可以正常工作,但它的功能正确性却依赖于:父类的addAll是在它的add方法上实现的。
这种“自用性”是实现细节,不是承诺。父类不承诺这个实现永远不变,而一旦发生改变,子类仍然可能无法正常工作。
如果子类覆盖addAll方法来遍历参数集合,为每个元素调用一次(已覆盖的)add方法,可以得到正确的结果,不管父类的addAll是否依赖add实现。因为父类的addAll实现将不会再被调用到。这实际上相当于重新实现了超类的方法。而这样做并不可靠,因为无法访问父类的私有域,可能有些方法无法实现。
转发(forwarding): 新类中的每个实例方法,都可以调用被包含的现有实例中对应的方法,并返回它的结果。这个过程称为转发。
下面重新实现上面的例子:
import java.util.*;
//可重用的转发类
public class ForwardingSet implements Set {
private final Set s;
public ForwardingSet(Set s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection> c)
{ return s.containsAll(c); }
public boolean addAll(Collection extends E> c)
{ return s.addAll(c); }
public boolean removeAll(Collection> c)
{ return s.removeAll(c); }
public boolean retainAll(Collection> c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o)
{ return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
import java.util.*;
//实现类
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set s) {
super(s);
}
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
public static void main(String[] args) {
InstrumentedSet s =
new InstrumentedSet(new HashSet());
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
这正是装饰模式(Decorator)。
如果不确定,一律考虑用复合的方式。
Java平台类库中,有一些违反这条原则的地方。比如stack并不是vector,所以Stack不应该扩展Vector。