Effective Java 复合优先于继承

继承是实现代码重用的有力手段,但是使用不当会导致软件变得脆弱。在包的内部使用继承是非常安全的,子类和超类的实现都处在同一个程序员的控制之下。对于专门为了继承而设计、并且具有很好的文档说明的类来说,使用继承也是非常安全的。然而们对于进行跨越包边界的继承,则要非常小心。“继承”在这里特指一个类扩展另一个类。

 

复制代码

public class InstrumentedHashSet<E> extends HashSet<E> {

    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("s","a","p"));
        System.out.println(s.getAddCount());
    }

}

复制代码

考虑上面的提供计数功能的HashSet。我们希望得到的结果是3,但结果却是6,原因是addAll方法是基于add方法实现的,InstrumentedHashSet的addAll方法首先将addCount加3,然后super.addAll调用HashSet的addAll方法,而addAll又分别调用到InstrumentedHashSet覆盖了的add方法,每个元素调用一次,又给addCount加了3,结果是6。

只要去掉覆盖的addAll方法就可以修正问题,虽然这个类可以正常工作了,但是HashSet的addAll方法是实现细节,不是承诺,意味着不能保证addAll的实现方法在未来发行的版本保持不变。

 

稍微好一点的方法是覆盖addAll方法来遍历指定的集合,为每个元素调用一次add方法,这样可以保证得到正确的结果,然而这种方法对于要访问超类的私有域就无能为力了。

覆盖超类的每一个添加元素的方法,如果超类以后增加一种新的添加元素方法,那就可能有“非法的元素”添加到集合中。

 

不扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例,可以避免前面提到的问题,这种设计称为“复合”,因为现有的类变成了新类的一个组件,新类中的每个实例方法都可以调用被包含的现有类实例中对应的方法,并返回它的结果。

复制代码

public class ForwardingSet<E> implements Set<E> {
    private Set<E> s;
    
    public ForwardingSet(Set<E> s) {
        this.s = s;
    }

    public void clear() {
        s.clear();
    }
    public boolean contain(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 retainAll(Collection<?> c) {
        return s.retainAll(c);
    }
    public Object[] toArray() {
        return s.toArray();
    }
    public boolean equals(Object o) {
        return s.equals(o);
    }
    public int hashCode() {
        return s.hashCode();
    }
    public String toString() {
        return s.toString();
    }
    @Override
    public boolean contains(Object o) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public <T> T[] toArray(T[] a) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public boolean removeAll(Collection<?> c) {
        return s.removeAll(c);
    }
      
}

复制代码

 

复制代码

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);
    }
    
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }
    
    public int getAddCount() {
        return addCount;
    }

}

复制代码

ForwardingSet作为一个转发类,拥有一个Set实例变量,而InstrumentedSet把add和addAll方法覆盖掉,实现计数功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值