本文所说的继承都是类和类之间的继承,而非接口和类之间的继承
继承是面向对象的三大特性之一。一般情况下,当子类和父类在同一包下且必须继承的时候可以采用继承,毕竟同一包下的代码由同一个人管理;另外当某个类就是被设计成去继承的时候,也可以考虑继承。
但是继承如果使用不当的话,会导致随后软件修改很困难。
假设我们需要实现Set
中曾经加入过多少次元素。实现如下:
public class InstrumentedHashSet<E> extends HashSet {
private long count;
@Override
public boolean add(Object o) {
count++;
return super.add(o);
}
@Override
public boolean addAll(Collection collection) {
count += collection.size();
return super.addAll(collection);
}
public long getCount() {
return count;
}
public static void main(String[] args) {
HashSet hashSet = new HashSet(3);
hashSet.add(1);
hashSet.add(2);
hashSet.add(3);
InstrumentedHashSet<Integer> instrumentedHashSet = new InstrumentedHashSet<>();
instrumentedHashSet.addAll(hashSet);
//结果输出为6,而非3
System.out.println(instrumentedHashSet.getCount());
}
}
但是结果很不幸,不为3
,而是6
。因为在父类中,addAll
调用的就是add
方法,这样addAll
加了3
,然后还会在add
方法中加3次,最终得到6
。
我们可以在InstrumentedHashSet
中不重写addAll
方法,这样得到count=3
,但是随后HashSet
中addAll
方法的实现究竟依不依赖于add
方法,我们不得而知,可能修改了addAll
的实现之后我们忘记修改InstrumentedHashSet
类。
另外,也可以在InstrumentedHashSet
中重新实现addAll
方法,而不是override
。但这样实现太麻烦了,相当于需要重新实现父类中的addAll
方法,不符合重复一次规则。
为了避免以上的问题,我们推荐采用复合:将父类作为新类的成员属性。
采用复合改写上面的代码:
ForwardingSet.java
public class ForwardingSet<E> implements Set<E> {
private Set<E> s;
public ForwardingSet(Set<E> s) {
this.s = s;
}
@Override
public int size() {
return s.size();
}
@Override
public boolean isEmpty() {
return s.isEmpty();
}
@Override
public boolean contains(Object o) {
return s.contains(o);
}
@Override
public Iterator iterator() {
return s.iterator();
}
@Override
public Object[] toArray() {
return s.toArray();
}
@Override
public boolean add(E o) {
return s.add(o);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean addAll(Collection collection) {
return s.addAll(collection);
}
@Override
public void clear() {
s.clear();
}
@Override
public boolean removeAll(Collection collection) {
return s.removeAll(collection);
}
@Override
public boolean retainAll(Collection collection) {
return s.retainAll(collection);
}
@Override
public boolean containsAll(Collection collection) {
return s.containsAll(collection);
}
@Override
public Object[] toArray(Object[] objects) {
return s.toArray(objects);
}
}
InstrumentedHashSet.java
public class InstrumentedHashSetV2<E> extends ForwardingSet<E> {
private int count;
public InstrumentedHashSetV2(Set<E> s) {
super(s);
}
@Override
public boolean add(E o) {
count++;
return super.add(o);
}
@Override
public boolean addAll(Collection collection) {
count += collection.size();
return super.addAll(collection);
}
}
这里,ForwardingSet.java
俗称转发类(Forwarding),是设计用来复用的。真正的包装类还是InstrumentedHashSet.java
。