package com.laz.learning;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public class ForwardingSet<E> {
private Set<E> s;
public ForwardingSet(Set<E> s) {
this.s = s;
}
public int size() {
return s.size();
}
public boolean isEmpty() {
return s.isEmpty();
}
public boolean contains(Object o) {
return s.contains(o);
}
public Iterator<E> iterator() {
return s.iterator();
}
public Object[] toArray() {
return s.toArray();
}
public <T> T[] toArray(T[] a) {
return s.toArray(a);
}
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 retainAll(Collection<?> c) {
return s.retainAll(c);
}
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
public void clear() {
s.clear();
}
@Override
public boolean equals(Object obj) {
return s.equals(obj);
}
@Override
public int hashCode() {
return s.hashCode();
}
@Override
public String toString() {
return s.toString();
}
}
package com.laz.learning;
import java.util.Collection;
import java.util.Set;
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
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;
}
}
如果在扩展一个类的时候,仅仅是增加了一些新的方法,而不覆盖现有的方法,你可能为认为是安全的。虽然这种扩展方式比较安全一些,但也并非完全没有风险。如果类在后续的发行版本中获得了一个新的方法,并且不幸的是,你给子类提供了一个签名相但返回类型不同的方法,那么这样的子类将无法通过编译。如果给子类提供 的方法带有与新的超类方法完全 相同的签名和返回类型,实际上就覆盖了超类中的方法,因此又回到了上述的两个问题上去了。此外,你的方法是否能够遵守新的超类方法的约定,这也是很值得怀疑的,因为当你在编写子类方法的时候,这个约定根本没有面世。
幸运的是,有一种办法可以避免前面提到的所有的问题。不用扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例。这种设计被称为“复合”,因为现有的类变成了新类的一个组件。新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回它的结果。这被称为“转发”,新类中的方法被称为转发方法。这样得到的类将会非常稳固,它不依赖于现有类的实现细节
继承的功能非常强大,但是也存在许多的问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类型关系时,使用继承是最适当的。即使如此,如果子类和超类处在不同的包中,并且超类并不是为了继承而设计的,那么继承将会导致脆弱性。为了避免这种脆弱性,可以用复合和转发机制来代替继承,尤其是当存在适当的接口可以实现包装类的时候。包装类不仅比子类更加健壮,而且 功能 也更加强大。
需要注意一点:包装类不适合用在架设框架中;在回调框架中,对象把自身的引用 传递给其它的对象,用于后续的调用。因为被包装起来的对象并不知道它外面的包装对象。这被称为SELF问题
摘抄自:Effective Java