2012-09-02 63 views
10

當我讀到一本Java書時,作者曾經說過,在設計一個類時,使用equals()來繼承是非常不安全的。例如:爲什麼我不應該用繼承來使用equals?

public final class Date { 
    public boolean equals(Object o) { 
     // some code here 
    } 
} 

在上面的類,我們應該把final,所以其他類不能從這個繼承。而我的問題是,爲什麼當它允許另一個類繼承它是不安全的?

+0

'Comparable'指定'compareTo',而不是'equals'。 – oldrinb

+0

哦。謝謝:)我編輯過:D – hqt

+0

這本書叫什麼名字? –

回答

19

因爲很難(不可能?)使它正確,尤其是symmetric property

假設您有Vehicle類和Car extends Vehicle類。 Vehicle.equals()如果參數也是Vehicle並且具有相同的權重,則產生true。如果你想實現Car.equals()它應該產生true僅當參數也是一個車,除了重量外,還應做比較,發動機等

現在想象一下下面的代碼:

Vehicle tank = new Vehicle(); 
Vehicle bus = new Car(); 
tank.equals(bus); //can be true 
bus.equals(tank); //false 

如果通過重合坦克和公共汽車具有相同的重量,第一次比較可能產生true。但由於坦克不是汽車,因此將它與汽車相比總會產生false

你有一些變通辦法:

  • 嚴格:兩個對象相等當且僅當他們有完全相同的類型(以及所有屬性都相等)。這很糟糕,例如當你僅僅爲了添加一些行爲或裝飾原始類而進行子類化時。一些框架也在不需要注意的情況下繼承類(Hibernate,使用CGLIB代理的Spring AOP ...)

  • 鬆散:如果兩個對象的類型「兼容」並且它們具有相同內容(語義上),則兩個對象相等。例如。如果它們包含相同的元素,則兩個集合是相等的,一個是HashSet而另一個是TreeSet(感謝@veer用於指出)並不重要。

    這可能會引起誤解。取兩個LinkedHashSet(其中插入順序作爲合同的一部分)。然而,由於equals()只需要原料Set合同考慮,比較收益率true即使是明顯不同的對象:

    Set<Integer> s1 = new LinkedHashSet<Integer>(Arrays.asList(1, 2, 3)); 
    Set<Integer> s2 = new LinkedHashSet<Integer>(Arrays.asList(3, 2, 1)); 
    System.out.println(s1.equals(s2)); 
    

又見

+1

不*不可能*;見例如['Set.equals'](http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#equals(java.lang.Object)) – oldrinb

+0

@veer:我更新了我的回答,謝謝。 –

+0

@TomaszNurkiewicz作爲你的鏈接給我,如果我們使用if(this.getClass()!= tank.getClass()){return false;}或if(this.getClass()!= tank.getClass() ){return false;}',問題就解決了。你能告訴我更多關於這個案子嗎? – hqt

8

馬丁·奧德斯基(中Java中泛型的背後人物以及當前的原始代碼庫)在他的書中有一個很好的章節編程Scala解決這個問題。他建議添加一個canEqual方法可以解決相等/繼承問題。你可以在他的書的第一版,這是可以在線閱讀的討論:

Chapter 28 of Programming in Scala, First Edition: Object Equality

這本書當然是指斯卡拉的,但同樣的想法適用於經典的Java。對於來自Java背景的人來說,示例源代碼不應太難理解。

編輯:

它看起來像Odersky的早在2009年發表了在Java中同一概念的文章,它可在同一網站上:

How to Write an Equality Method in Java

我真的不要以爲在這個答案中總結這篇文章會做正義。它涵蓋了深入討論對象平等的主題,從平等實現中的常見錯誤到Java equals作爲等價關係的全面討論。你應該真的只是讀它。

相關問題