类和接口——复合优先于继承
继承与复合选择
有一个具备一定功能类,我们要对其功能进行拓展,到底是采用复合呢还是继承呢?当新类与旧类的关系是从属关系是,即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看什么视频好呢?视频经验大总结
