item 16在描述复合之前给出了InstrumentedHashSet 类由于继承了HashSet类导致计数结果翻倍的缘由:HashSet实现Set接口时,改造了Set的addAll方法,在HashSet类中,addAll()方法中调用了add方法,由于InstrumentedHashSet中改写了add方法,转而执行子类的add方法,恰恰add方法中同样会计数,导致结果翻倍
如何理解“这时候有一种方案可以避免以上的所有问题。不用扩展现有的类,而是在新的类中增加一个私有对象,它引用现有对象的一个实例,这种设计被称作为复合,因为现有的类变成了新类的一个组件,新类中的每个实力方法都可以调用被包涵的现有类实例中对应的方法,并返回它的结果,这称为转发,新类中的方法被称为转发方法。”
也就是说,因为HashSet改造的addAll方法无法满足我们的需求,现在重写一个类来实现Set接口,因为Set接口中addAll方法与add方法不相关,但是Set中的方法太多了,想想就因为要用add与addAll要加上很多代码挺头疼的,这时候使用一个中间类做桥梁,在这个类中定义一个Set对象,类中的所有方法都会转去执行Set对象里的方法,这叫做转发,Set对象叫做“被包含的现有类实例"
package ClassTest;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
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 Object[] toArray() {return s.toArray();}
public T[] toArray(T[] a) {return s.toArray(a);}
public boolean retainAll(Collection> c) {return s.removeAll(c);}
public boolean removeAll(Collection> c) {return s.removeAll(c);}
}
package ClassTest;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class InstrumentedHashSet extends ForwardingSet{
private int addCount = 0;
public InstrumentedHashSet(Set s){
super(s);
}
public boolean add(E e){
addCount++;
return super.add(e);
}
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 demo = new InstrumentedHashSet(new HashSet());
demo.addAll(Arrays.asList("Snap","Crackle","Pop"));
System.out.println(demo.getAddCount());
}
}
InstrumentedHashSet叫做包装类,将另一个Set实例包装起来了,是一个具有计数功能的另一个Set
前面提到的继承只适用于单具体的类,并且对于超类中所支持的每个构造器都要求有一个单独的构造器,与此不同的是,这里的包装类可以用来包装任何Set实现,并且结合任何先前的构造器一起工作。
InstrumentedHashSet甚至可以用来临时替换一个原本没有计数特性的Set实例
static void walk(Set dogs){
InstrumentedHashSet idogs = new InstrumentedHashSet(dogs);
}