类和接口——复合优先于继承
继承与复合选择
有一个具备一定功能类,我们要对其功能进行拓展,到底是采用复合呢还是继承呢?当新类与旧类的关系是从属关系是,即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 } }
复合与继承其他不同点
继承的方法只能扩大访问权限, 不能减少,而复合可以更方便地任意调整方法权限
继承只能增加方法, 不能减少方法:特殊情况下, 也只能通过异常等手段禁止访问对应方法。而复合可以很方便地只提供某些方法
下一篇:
学Java看什么视频好呢?视频经验大总结