类和接口——复合优先于继承

继承与复合选择

有一个具备一定功能类,我们要对其功能进行拓展,到底是采用复合呢还是继承呢?当新类与旧类的关系是从属关系是,即cat is an animal,English book is a book,我们优先使用继承;当新类是旧类的组成部分之一时,即hand is a part of body,jiangsu is a part of China,我们优先使用复合

理由如下:当我们要扩展一个类时,特别是一个别人写好的类,一个类库的类,我们往往关心的仅仅是单个api的功能,而不关心他的实现,但是存在的一个问题就是,同一个类的各个方法直接可能存在联系,可能一个方法的实现依赖于另一个方法,这就意味着,当我们调用一个我们想要操作的方法时,“继承”会隐式的调用另一个方法,这就可能存在问题


经典例子是Set中add()和addAll()的内在联系

需求:新建一个集合类,维护一个addCount变量,记录,一共添加了多少次新值。分别用继承,复合实现。

使用继承:

public class MySet<E> extends HashSet<E>{
    private int addCount = 0;

    public MySet() {
    }

    @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 class TestDemo {
    public static void main(String[] args) {
        MySet<String> set = new MySet<String>();
        List<String> list = new ArrayList<String>();
        list.add("abc");
        list.add("def");
        list.add("ghi");

        set.addAll(list);
        System.out.println(set.getAddCount());
        //the ans is 6
    }
}

因为,在hashSet的addAll()实现中,是循环调用add()方法的,所以导致3*2。当然你也可以重写addAll()方法,但是这样就失去了继承的意义。

使用复合:

public class MySet2<E>{

    private  Set<E> s = new HashSet<>();

    private int addCount = 0;

    public boolean add(E e) {
        addCount++;
        return s.add(e);
    }

    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return s.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }

}
public class TestDemo {
    public static void main(String[] args) {
        MySet2<String> set = new MySet2<>();
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");
        list.add("ghi");

        set.addAll(list);
        System.out.println(set.getAddCount());
        //the ans is 3
    }
}

复合与继承其他不同点

继承的方法只能扩大访问权限, 不能减少,而复合可以更方便地任意调整方法权限

继承只能增加方法, 不能减少方法:特殊情况下, 也只能通过异常等手段禁止访问对应方法。而复合可以很方便地只提供某些方法

经验分享 程序员 微信小程序 职场和发展