本文所說的繼承都是類和類之間的繼承,而非接口和類之間的繼承
繼承是面向對象的三大特性之一。一般情況下,當子類和父類在同一包下且必須繼承的時候可以採用繼承,畢竟同一包下的代碼由同一個人管理;另外當某個類就是被設計成去繼承的時候,也可以考慮繼承。
但是繼承如果使用不當的話,會導致隨後軟件修改很困難。
假設我們需要實現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
。