或者,添加一个公有方法来返回该数组的一个备份
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方法,或拷贝构造器。
不可变对象为其他对象提供了大量的构件(building blocks)。唯一的缺点是,对于每个不同的值,都需要一个单独的对象,可能导致性能问题。有些类不能做出不可变的,仍然应该尽可能的限制他的可变性。 除非有足够的理由使域变成非final的,否则,每个域都应该是final。 不要在构造器和静态工厂之外,再额外的提供初始化方法,除非有令人信服的理由必须这么做。
继承打破了封装性,子类依赖于父类中特定功能的实现细节。 考虑下面的InstrumentedHashSet类,扩展了HashSet,用addCount来记录已添加的元素数量。
import java.util.*; public class InstrumentedHashSet<E> extends HashSet<E> { // 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<String> s = new InstrumentedHashSet<String>(); 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<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> 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<E> 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> 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<E> 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<String> s = new InstrumentedSet<String>(new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }这正是装饰模式(Decorator)。
如果不确定,一律考虑用复合的方式。 Java平台类库中,有一些违反这条原则的地方。比如stack并不是vector,所以Stack不应该扩展Vector。