2015-06-26 141 views
11

我正在閱讀J. Bloch的有效的Java現在我在繼承vs組合部分。據我瞭解,他說繼承並不總是好的。Java繼承vs初始化

子類中脆弱性的相關原因是它們的超類 可以在後續版本中獲取新方法。假設程序 取決於其安全性,因爲所有插入到 某些集合中的元素都滿足某些謂詞。這可以通過 被保證子類的收集和重寫能夠 每個方法添加元素,以確保謂詞滿足之前 添加元素。這工作正常,直到新方法能夠 插入元素被添加到超類在隨後的 版本。

但是它爲什麼不起作用?超類只是接口,如果我們添加一個新的方法,我們只是一個編譯時錯誤。這不是有害的...

+1

向super *類*添加一個新方法不會導致這樣的編譯時錯誤。事實上,將'default'方法添加到接口中也不會產生編譯時錯誤。 –

+0

如果你正在談論'java.util.Collection',那不是一個超類。這只是一個界面。 – Alderath

+0

如果子類被重新編譯,那麼向超類或接口添加抽象方法是編譯時錯誤,但這可能不會發生。 –

回答

19

假設你有一些庫V1.0集合超:

public class MyCollection { 
    public void add(String s) { 
     // add to inner array 
    } 
} 

你繼承它以僅接受具有長度爲5字符串:

public class LimitedLengthCollection extends MyCollection { 
    @Override 
    public void add(String s) { 
     if (s.length() == 5) { 
      super.add(s); 
     } 
    } 
} 

合同,不變該類不會包含長度不爲5的字符串。

現在版本2.0的庫已發佈,並且您將開始使用它。基類修改爲:

public class MyCollection { 
    public void add(String s) { 
     // add to inner array 
    } 

    public void addMany(String[] s) { 
     // iterate on each element and add it to inner array 
    } 
} 

並且您的子類保持不變。現在您的子類的用戶可以做

LimitedLengthCollection c = new LimitedLengthCollection(); 
c.addMany(new String[] {"a", "b", "c"}); 

並且您的子類的合同因此被破壞。它應該只接受長度爲5的字符串,而現在它已經不存在了,因爲在超類中添加了一個額外的方法。

+0

儘管這是問題的核心(脆弱的基類),但只有在addMany方法不使用add方法,而是實現本身的添加行爲時纔會發生。也許你應該爲所有這些方法添加一些實現細節。 – Seelenvirtuose

+2

正確。這會導致另一個問題。版本2.0可以委託給add(),並且你可以依靠它僅在add()中進行檢查,而版本3.0不會再委託給add(),因此你的檢查將被繞過。 –

+0

所以,這裏的關鍵是'MyCollection'是一個具體的類。得到它了。但在Java 8中,實現庫的接口仍然安全嗎?他們可以爲某些方法提供默認實現,如果我們在版本1.0和版本2.0中實現其中的一個方法,則會添加一些方法,但默認實現也可能會陷入困境(正如您指出的那樣)。 –

2

因爲它(通常)會破壞已實現Collection類的客戶端代碼。

在這個特定的例子中,安全性將被破壞,因爲惡意用戶可以通過使用發佈代碼後添加的非覆蓋方法來插入項目。

將你的代碼放在你不控制的繼承類上可能會在將來咬你。

+0

在這種情況下這將是一件好事;如果客戶端代碼中斷(無法編譯),則需要在運行之前對其進行修復,這意味着避免了安全威脅。 –

+0

你能想象Oracle在後一點改變Collection類/接口嗎?那會是什麼問題? – idipous

+0

我並不反對改變這樣的公共接口是一件壞事;我只是指出,這個問題是關於安全問題的,在這種情況下,如果出現代碼中斷會比導致安全問題更好。 –

2

如果我們添加一個新的mehtod我們只是一個編譯時錯誤

那是真的,只有當一個抽象方法加入到超/接口。如果添加了非抽象方法,則不會覆蓋該新方法是完全有效的。

3

問題不是繼承無法正常工作。

問題是,對於繼承,開發人員無法強制執行某些行爲(例如滿足某些謂詞的集合示例)。

當我們創建一個新類時,很少它確實是另一類的特殊類型。更常見的是使用其他類的新東西。

因此,我們很少需要繼承,更多的時候我們需要創建一個使用其他類來創建類的類。

是阿 VS

你要問自己:

B類是一類新的子類型做A的同樣的事情用不同的方式?

B類有一個類裏面做什麼是intented做從 不同的東西嗎?

而且知道更多的時候正確的答案是後者。