第四章 類和介面 第16條 復合優先於繼承

2021-08-20 06:40:55 字數 2906 閱讀 7140

本條目討論的問題並不適用於介面繼承

與方法呼叫不同的是,繼承打破了封裝性,如果超類中特定功能的實現細節發生了變化,子類可能會遭到破壞,即使它的**完全沒有改變,除非超類是專門為了擴充套件而設計的,並且有很好文件說明

例項:

public class instrumentedhashsetextends hashset

public instrumentedhashset(collection<? extends e> c)

public instrumentedhashset(int initialcapacity, float loadfactor)

public instrumentedhashset(int initialcapacity)

@override

public boolean add(e e)

@override

public boolean addall(collection<? extends e> c)

public int getaddcount()

}

這個類看起來正常,實際上它並不能工作

instrumentedhashset ih = new instrumentedhashset<>();

ih.addall(arrays.aslist(new string ));

system.out.println(ih.getaddcount());

我們期望的結果是3,但是實際結果是6,這是因為 addall呼叫了add方法

add方法這種"自用性",是實現細節,不是承諾,它不能保證在所有實現類中不變,不能保證隨著發行版本的不同而發生變化,所以這種類是非常脆弱的

稍微正確的做法是,子類模仿超類的方式,在addall中呼叫add,這樣能得到正確的結果, 這樣做困難,而且容易出錯:最重要的一點,子類無法訪問超類的私有域,所以有的方法沒辦法使用

還有乙個安全問題:子類先擴充套件 超類在後期的維護中新增了這個方法,那麼使用多型的時候,子類可能被破壞,發生意想不到的結果

public abstract class _no 

public void use()

}

public class _no1 extends _no

public static void main(string args)

}

父類方法執行受阻,子類被無端呼叫

如果不覆蓋父類,只進行擴充套件,這種方式比較安全一些,但也不完全沒有風險,如果超類中的新增了乙個和子類中方法名相同的方法,但是返回結果不同,那麼就無法通過編譯,即使相同,又會回到上面的安全問題

解決辦法:

public class instrumentedhashsetextends forwardingset

private int addcount = 0;

@override

public boolean add(e e)

@override

public boolean addall(collection<? extends e> c)

public int getaddcount()

}

public class forwardingsetimplements set

@override

public int size()

@override

public boolean isempty()

@override

public boolean contains(object o)

@override

public iteratoriterator()

@override

public object toarray()

@override

public t toarray(t a)

@override

public boolean add(e e)

@override

public boolean remove(object o)

@override

public boolean containsall(collection<?> c)

@override

public boolean addall(collection<? extends e> c)

@override

public boolean retainall(collection<?> c)

@override

public boolean removeall(collection<?> c)

@override

public void clear()

}

在新類中增加乙個私有域,它引用現有類的乙個例項,這種設計被稱做"復合",因為現有的類變成了新類的乙個元件,新類中的每個方法都可以呼叫現有類例項對應的方法,並返回它的結果.這被稱為**,新類中方法被稱為**方法,這樣可以實現很好的分離,注意這個實現分為兩部分:類本身和可重用的**類,包含所有的**方法,沒有其它方法

包裝類的優勢:可以實現多型,接收任何set 及其子類,為其加上計數方法

**類的優勢:可重用,解除耦合

測試:

instrumentedhashset ih = new instrumentedhashset<>(new treeset<>());

ih.addall(arrays.aslist(new string ));

system.out.println(ih.getaddcount());

總結:

繼承需要慎用:考慮清楚是否是"is-a"的關係

第四章 類和介面

public protected 包級私有 private 退化類 就是一些集中例項域但是沒有任何行為的類,個人理解就是只有欄位而沒有方法。這一章更多的是講解一些程式設計時候的規範。1.訪問性的最小化 這種形式有利於封裝,同時更少的對外提供修改的渠道,也是為了更好的保護。同時,對外提供的方法,不會讓...

第四章 復合型別

第一題 按示例請求顯示資訊。第二題 修改程式4.4,使用c string類 include include using namespace std int main 第三題 使用char陣列,和cstring中的函式,按示例格式顯示輸出 include include using namespace...

C Primer Plus 第四章 復合型別

這兩天忙的厲害,閱讀計畫有點沒有按時完成哦,週末感冒,作業擠的荒,o o.哈哈堅持!現在回過來看,說明 c primer plus前面的至少六章都和c關聯很大。覺得很多東西一下子清晰了不少,而且在近期的幾次作業中也能得到應用,感覺真不錯。可見反覆的力量!還有一點就是一定要把課後的習題一步步做完,提高...