在【Effective Java】條16:複合優於繼承中我們已經提到過使用繼承的缺點。但如果是專爲繼承設計的,並提供文檔說明,那是排除在外的情形。下面看看怎麼專爲繼承設計類,並怎麼提供文檔說明。
該類的文檔需明確說明重寫任何方法的影響
對於每個public
或者protected
的方法或者構造器,都需要指明該方法或者構造器調用了哪些可覆蓋的方法,以什麼順序,每個調用結果是如何影響後續的處理。最好還需要說明在哪種情形下類會調用可重寫的方法。在
JDK
中,針對調用了可重寫方法的方法,註釋中在描述方法是做什麼的文檔後面,通常都添加了調用的說明,且通常都以This implementation
開頭。如java.util.AbstractCollection
中remove
方法,說明remove
是依賴於iterator
的remove
方法的:public boolean remove(Object o) Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call). This implementation iterates over the collection looking for the specified element. If it finds the element, it removes the element from the collection using the iterator's remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection's iterator method does not implement the remove method and this collection contains the specified object.
類可以通過
protected
方法或者protected
域來提供鉤子,關聯到內部工作流程中。如java.util.AbstractList
中的removeRange
方法:protected void removeRange(int fromIndex, int toIndex) Removes from this list all of the elements whose index is between fromIndex, inclusive, and toIndex, exclusive. Shifts any succeeding elements to the left (reduces their index). This call shortens the list by (toIndex - fromIndex) elements. (If toIndex==fromIndex, this operation has no effect.) This method is called by the clear operation on this list and its subLists. Overriding this method to take advantage of the internals of the list implementation can substantially improve the performance of the clear operation on this list and its subLists. This implementation gets a list iterator positioned before fromIndex, and repeatedly calls ListIterator.next followed by ListIterator.remove until the entire range has been removed. Note: if ListIterator.remove requires linear time, this implementation requires quadratic time. Parameters: fromIndex - index of first element to be removed toIndex - index after last element to be removed
removeRange
方法對於List
實現的最終用戶並無多大意義。提供該方法的唯一目的在於使子類更易於提供對字列表的快速clear方法。若沒有removeRange
方法,當在字列表上調用clear方法時,子類將不得不用平方級的時間來完成工作。==不是特別明白==如果類是爲繼承設計,那麼最好的方法就是自己寫子類來測試是否暴露了不該暴露的接口
當然,在設計類繼承時,也需要注意以下幾點:
1. 構造器不應該調用(直接或間接)可重寫的方法。舉個反例:
“`
public class Super {
// Broken - constructor invokes an overridable method
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
public final class Sub extends Super {
private final Date date; // Blank final, set by constructor
Sub() {
date = new Date();
}
// Overriding method invoked by superclass constructor
@Override public void overrideMe() {
System.out.println(date);
}
//輸出null和當前時間。因爲先調用父類構造器。
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
```
2. 實現clone
或者Serializable
接口的類,clone
和readObject
方法都不能調用可重寫的方法(類似於構造器)
3. 如果爲繼承設計的類實現Serializable
接口,則readResolve
或writeReplace
方法需要用protected
修飾,而不是private
,否則會被子類忽略