2010-06-08 65 views
14

它是否與舊版(非通用版)Collection保持向後兼容?還是有一個更微妙的細節,我失蹤了?我在remove也看到這種模式(remove(Object o)),但add被通用化爲add(E e)爲什麼我們要包含(Object o)而不是Containers(E e)?

+2

可能的重複[爲什麼不是Java集合刪除方法通用?](http://stackoverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic) – newacct 2010-06-08 02:18:48

回答

10

需要Object,因爲它匹配的對象不必與您傳入的對象類型相同;它只需要它們是平等的。從,contains(o)的規範中返回true,如果有對象e使得(o==null ? e==null : o.equals(e))爲真。請注意,沒有任何要求oe是同一類型。這是因爲equals()方法採用Object作爲參數,而不僅僅是與對象相同的類型。

雖然很多類定義了equals(),這樣它的對象只能等於它自己類的對象,但通常情況並非總是如此。例如,List.equals()的規範說,如果兩個List對象都是List s,並且具有相同內容,即使它們是List的不同實現,它們也是相等的。所以回到這個問題的例子中,有可能有一個Collection<ArrayList>,我可以用LinkedList作爲參數調用,如果列表中有相同的內容,它可能會返回true。如果是通用的並且將其參數類型限制爲E,則這將不可能。

事實上,接受任何對象作爲參數的事實允許一個有趣的使用,你可以用它來測試集合中的一個對象,滿足某些屬性的存在:

Collection<Integer> integers; 
boolean oddNumberExists = integers.contains(new Object() { 
    public boolean equals(Object e) { 
     Integer i = (Integer)e; 
     if (i % 2 != 0) return true; 
     else return false; 
    } 
}); 
+3

此方法簽名'包含(對象o)'提供了一個非常好的槍,可以方便地拍攝自己的腳。這種方法實際上是Java泛型限制的展示。 – 2010-06-08 13:21:40

4

這是因爲contains功能利用equals功能和equals功能是在基對象類中定義與equals(Object o)而非equals(E e)簽名(因爲不是所有的類是通用的)。與remove函數的情況相同 - 它使用帶有Object參數的equals函數遍歷集合。

但是,這並不直接解釋這個決定,因爲他們可能仍然使用E類型,並允許它在equals的調用中被自動強制類型爲Object;但我想他們希望允許在其他對象類型上調用該函數。有一個Collection<Foo> c;,然後調用c.contains(somethingOfTypeBar)沒什麼錯 - 它總是會返回false,所以它不需要強制類型輸入Foo(可以拋出異常),或者爲了防止異常,調用typeof。所以你可以想象,如果你正在迭代混合類型的東西,並且在每個元素上調用contains,那麼你可以簡單地在所有元素上使用contains函數,而不是需要守衛。

它實際上是讓人想起了「新的」弱類型語言,當你看它這樣...

+0

這沒有任何與真正的原因。對'contains'和也許'remove'進行某種類型的檢查會更好。這本來就不太容易出錯。 – 2010-06-08 01:38:29

+3

「它總會返回錯誤」不,它不會。一個班級完全有可能爲另一個班級的對象設置一個對象 – newacct 2010-06-08 02:01:46

0

因爲否則它只能與參數類型的完全匹配進行比較,特別是通配集合將停止工作,例如,

class Base 
{ 
} 

class Derived 
    extends Base 
{ 
} 

Collection< ? extends Base > c = ...; 

Derived d = ...; 

Base base_ref = d; 

c.contains(d); // Would have produced compile error 

c.contains(base_ref); // Would have produced compile error 

編輯
對於誰覺得這是不是一個原因持懷疑態度,這是一個帶將泛型修改數組列表包含方法

class MyCollection<E> extends ArrayList<E> 
{ 
    public boolean myContains(E e) 
    { 
     return false; 
    } 
} 

MyCollecttion< ? extends Base > c2 = ...; 

c2.myContains(d); // does not compile 
c2.myContains(base_ref); // does not compile 

基本上contains(Object o)是一個黑客,使這個非常常見的用例與Java Generics一起工作。

+0

小心解釋downvote? – 2010-06-08 13:13:08

+0

在正常情況下,沒有人會想將一個集合轉換爲'Collection <?在元素操作期間擴展Foo>'。如果是這種情況,'add(E e)'方法也會失敗。 – Jai 2017-08-23 06:06:57

+0

@Jai。這是一個場景'void myMethod(Collection <?extends Foo> coll)'。如果在這裏你需要看看這個集合中是否包含'Foo'的某個版本,那麼你會達到這個答案中描述的限制。 – 2017-09-13 17:29:10

0

」那個籃子裏的蘋果是否含有這種橙色?「

顯然不能給出TRUE的答案。但仍有可能:

  1. 答案是錯誤的。
  2. 問題不是很好形成,它不應該通過編譯。

collection api選擇了第一個。但第二種選擇也會很有意義。像這樣的問題是99.99%的廢話問題,所以甚至不要問!

+0

這個問題不應該被認爲是不合格的,如果一個例如有兩個水果籃,並想知道一個籃子裏的水果是否與另一個水果相匹配。如果一個人知道一個籃子裏只裝蘋果,一個籃子裏只裝一個橘子,那麼將搜索匹配短路是有意義的,但假設一個籃子只包含蘋果,另一個包含混合水果。先詢問另一個籃子中每個水果的蘋果籃,而不首先驗證其類型,那麼比詢問每個水果之前詢問它是否是蘋果更容易。 – supercat 2012-12-03 18:08:59

+0

在我的例子中,參數已知是橙色的,因此這個問題聽起來很荒謬。在你的例子中,問題是「那一籃子蘋果是否含有這種水果?」,這是合法的。這個用例比較少見。我們可以重新設計API以允許您的問題,同時禁止我的問題,通過限制參數類型爲Apple的超類型。 – irreputable 2012-12-03 19:43:56

+0

在.Net中沒有任何機制可以通過它來限制一個參數,泛型或其他,是一種其他類型的超類型,我不認爲Java也有這樣的機制。這樣的事情往往會違反Liskov替代原則,因爲'Fruit'可用的方法應該可以與'Orange'一起使用。在.Net中有很多方法可以通過哪些方式使用'Obsolete'標籤來觸發編譯器在一些特定的靜態可識別的愚蠢的情況下,但我認爲這會比有幫助的更困惑。 – supercat 2012-12-03 19:56:34

相關問題