網頁

2020/4/5

Effective Java 3e - Item 19: Design and document for inheritance or else prohibit it 筆記

Effective Java 3e - Item 19: Design and document for inheritance or else prohibit it 繼承的設計與文件說明或禁止繼承 筆記。

設計用來被繼承的類別(a class designed for inheritance)(以下簡稱「被繼承類別」)時的注意事項。

如果被繼承類別中有引用自身(self-use)的可覆寫方法(overridable methods)必須在文件特別說明,例如利用javadoc的@implSpec註解。

設計被繼承類別時要慎選可覆寫方法,可覆寫方法越少越好。

撰寫子類別來測試被繼承類別。

被繼承類別的建構式中不應呼叫可覆寫的方法,也就是建構式中不應呼叫publicprotected方法,因為都能被子類別覆寫。 例如下面Super類別的建構式中呼叫自身的method1method2method3方法,若子類覆寫了這三個方法,子類初始化時父類建構式會先執行,在呼叫這三個方法時會變成呼叫成子類的實作而出錯。

public class Super {

    public Super() {
        method1(); // 不應在建構式呼叫自身的public方法
        method2(); // 不應在建構式呼叫自身的protected方法
        method3(); // 不應在建構式呼叫自身的default(無存取修飾)方法
        method4(); // 允許在建構式呼叫自身的private方法
    }

    public void method1() { ... } // overridable
    protected void method2() { ... } // overridable
    void method3() { ... } // overridable
    private void method4() { ... } // 無法被覆寫
}

若類別實作CloneableSerializable介面,則子類別繼承時必須注意。clone()readObject()方法中不可直接或間接呼叫可覆寫方法。

若被繼承類別實作Serializable且實作了readResolve()writeReplace()方法,則此二方法應為protected而非private,否則子類在序列化及反序列化時此兩方法會被忽略。

若類別非設計用來被繼承,且沒有繼承注意事項的文件說明,則此類別應該禁止被繼承。

避免類別被繼承的做法一是宣告類別為final

public final class ProhibitExtensionClass { // final類別無法繼承

    public ProhibitExtensionClass() {
    }

}

避免類別被繼承的做法二為建構式改為private或package-private(無修飾子)並以靜態工廠方法來產生實例。

public class ProhibitExtensionClass {

    private ProhibitExtensionClass() { // 子類別建構式中無法super(),因此無法繼承
    }
    
    public static ProhibitExtensionClass createInstance() { // 靜態工廠方法
        return new ProhibitExtensionClass();
    }

}

避免被繼承類使用自身覆寫方法(self-use overiddable method)的設計。

總之設計一個要被繼承的類別是困難的,否則建議使用組合設計替代繼承。


參考:

沒有留言:

張貼留言