[Note] Effective Java #15 除非專為繼承而設計並提供文件,否則不要使用繼承

為繼承而寫的class,必須提供文件來描寫override函式的效果,文件中必須說明對nonfinal而且public或protected能override的部份,提供詳細的說明。

如果函式有override或是call其它class的函式,依然要詳細註明,否則若是有使用者override該函式,容易因沒有註明而造成麻煩,例如:collection中的remove函式中說明會使用iterator,如果使用者覆寫iterator的部份,就易造成達不到預期結果的困擾。class的說明文件必須清楚的指出如果覆寫函式後會影響什麼部份,審慎的列出protected函式及欄位,一旦提出後,就代表往後的版本皆需要維持向下相容。
class需要遵守以下的限制與規範,才適合用於繼承。無論直接或間接,constructor不能call會被override的函式。superclass的constructor會先執行,這時override的函式在subclass中,subclass尚未被叫起,就會發生不如預期的錯誤,下面提供一範例,SubClass override在constructor中會用到的函式。
public class SuperClass {
    public SuperClass () {
        forOverride();
    }

    public void forOverride() {
        System.out.println("superclass.");
    }
}

public class SubClass extends SuperClass {
    private final long time;

    public SubClass() {
        time = System.currentTimeMillis();
        forOverride();
    }

    @Override
    public void forOverride() {
        System.out.println("subclass");
        System.out.println(time);
    }

    public static void main(String[] args) {
        SubClass c = new SubClass();
    }
}
執行main後的結果如下
subclass
0
subclass
1338219804843
首先SuperClass constructor先執行,接著執行forOverride函式,但是這時因為SubClass constructor尚未執行,使time尚未有值,接著SubClass constructor執行,指定time的值後,執行forOverride函式。這樣亦會造成final欄位的time居然有兩個不一樣的值。
Cloneable及Serializable會對繼承造成一些麻煩,clone()及readObject()都不能call能被override的函式,因為它們的行為類似constructor,以clone來說,override的函式會在subclass的clone之前先執行,這樣有可能會修改到物件,使clone後產生錯誤。(將clone想成上例的constructor,而forOverride為clone時call已override的函式)。Serializable的部份要在後面才會介紹。允許繼承並不是一見簡單的事,在對abstract class就明確的知道需要使用繼承,而對immutable class就不應該繼承。
關於一般的concrete class的情況,沒有特別提供來作為subclassing,要禁止被繼承,可以將constructor列為private或是default,再來用static factory來取代。

如果有錯誤,請留言告知,非常感謝。

沒有留言:

張貼留言