2013-11-25 77 views
2

我一直在測試一個代碼並偶然發現一個問題:你應該在子類中調用super.equals()方法,該方法可以覆蓋一些用於equals()超類的方法?繼承 - 在子類中使用super.equals()覆蓋在超類的equals方法中使用的方法

讓我們看看下面的代碼:

public abstract class Item { 
    private int id; 
    private float price; 

    public Item(int id, String name, float price, String category) { 
     this.id = id; 
     this.name = name; 
     this.price = price; 
     this.category = category; 
    } 

    public int getID() { 
     return id; 
    } 

    public float getPrice() { 
     return price; 
    } 

    @Override 
    public boolean equals(Object object){ 
     if(object instanceof Item){ 
      Item item = (Item) object; 
      if(id == item.getID() 
       && price == item.getPrice())      
      { return true; } 
     } 
     return false; 
    } 
} 

類和子類DiscountedItem:

public class DiscountedItem extends Item { 
    // discount stored in % 
    private int discount; 

    @Override 
    public boolean equals(Object object) { 
     if(object instanceof DiscountedItem){ 
      DiscountedItem item = (DiscountedItem) object; 
      return (super.equals(item) 
        && discount == item.getDiscount() 
      ); 
     } 
     return false; 
    } 

    public int getDiscount() { 
     return discount; 
    } 

    @Override 
    public float getPrice() { 
     return super.getPrice()*(100 - discount); 
    }  
} 

我一直在剛剛重新閱讀Angelika Langer's secrets of equals(),她甚至說:

如果類有一個超類Object以外的類,應該調用super.equals()。

但我認爲當子類重寫某些方法時,這是非常不可預測的。例如,當我使用equals比較2個DiscountedItem對象,超級方法被調用,item.getPrice()動態分派到正確的方法在子類中DiscountedItem,而其他價格值是使用變量直接訪問。

那麼,它是真的取決於我(因爲我應該正確實施該方法)還是有辦法解決它嗎?

+0

您的子類的equals方法應該以與該方法的契約一致的方式實現。委託給super.equals()的原因是子類可能不能訪問super.equals()可能依賴的父類字段。如果您違反封裝,所有投注都將關閉。如果您通過讓子類破壞父類的合約來實現您的'super.equals()',那麼所有投注都將關閉。 – Floegipoky

+0

有人可能會爭辯說,這裏的根本問題是將業務邏輯嵌入到bean獲取器中,而不是更多的學術問題。:) – Affe

+0

@Affe噢,這甚至不是真正的Web應用程序,它只是軟件測試中給出的一個學術示例類:)但我不認爲這個錯誤是有目的的.. –

回答

4

比較實例變量,而不是直接的實例變量與它相關的getter方法。

例如,更改

&& price == item.getPrice()) 

&& this.price == item.price) 

該吸氣劑的方法是不必要的,因爲私有實例變量僅類結構外部訪問。


注:

我以前提出以下建議:

&& this.getPrice() == item.getPrice()) 

儘管它會在問題的例子工作,它不適合於所有情況。試想,如果子類DiscountedItem聲明的方法getPrice這樣:

@Override 
public float getPrice() { 
    return Math.floor(super.getPrice()); 
} 

這會導致錯誤的等價:

DiscountedItem firstItem = DiscountedItem(1, "", 1.1, ""); 
DiscountedItem secondItem = DiscountedItem(1, "", 1.0, ""); 
firstItem.equals(secondItem); // Returns true despite different prices. 
+1

我同意@Floegipoky。應該直接比較類的成員變量與比較getter方法的結果。甚至當它們的變量值不相等時,也可以在比較它們的方法時使兩個對象相等。我會相應地更新我的答案。 – Matt

1

哦,我,我想我不得不發佈問題得到它。

爲了擺脫問題,不要直接訪問變量 - 調用getters!

@Override 
public boolean equals(Object object){ 
     if(object instanceof Item){ 
      Item item = (Item) object; 
      if(this.getID() == item.getID() 
       && this.getPrice() == item.getPrice())      
      { return true; } 
     } 
     return false; 
} 

此代碼在覆蓋方法時不再存在問題。

+0

是不是它總是如此工作。做得好。 – Matt

+0

@Matt每一個。單。時間。但是,嘿,你打我吧.. –

0

如果您在遇到問題時調用DiscountedItem的等號? 如果有東西是DiscountedItem和其他東西是這兩個 永遠不會等於等於。對?所以如果你總是打電話給獲得者,我不會看到任何問題。

此外,如果您覆蓋equals,則需要重寫hashCode。

+0

我不是想比較不同的對象,而是比較從他們的父母被篡改的對象的部分。 hashCode已經實現了,但是我沒有包含它,因爲它與這個問題沒有任何關係。 –

+0

對,是的,我的觀點是這些「對象的部分」總是「來自後裔類的對象的一部分」,所以我說我看不出有什麼問題(關於壓倒一切的方法)。 –

2

equals的執行應取決於狀態和類型,但不取決於功能。你在你的基類出了問題:

@Override 
public boolean equals(Object object){ 
    if(object instanceof Item){ // Type check- good! 
     Item item = (Item) object; 
     if(id == item.getID() // Depends on functionality- bad! 
      && price == item.getPrice()) // Depends on functionality- bad!      
     { return true; } 
    } 
    return false; 
} 

item.getID()item.getPrice(),因爲你已經注意到,可以覆蓋打破Item.equals()合同。

@Override 
public boolean equals(Object object){ 
    if(object instanceof Item){ // Type check- good! 
     Item item = (Item) object; 
     if(id == item.id // Depends on state- good! 
      && price == item.price) // Depends on state- good! 
     { return true; } 
    } 
    return false; 
} 

這將永遠不會被子類破壞。此外,它可以讓孩子有意義地委託給它。

@Override 
public boolean equals(Object object) { 
    if(object instanceof DiscountedItem){ 
     DiscountedItem item = (DiscountedItem) object; 
     return (super.equals(item) 
       && this.discount == item.discount 
     ); 
    } 
    return false; 
} 

孩子只需要擔心比較它擁有的數據。

+1

感謝您的回答,您當然是對的!通常,當我編寫代碼時,我會這樣做,就像你所建議的那樣,但是直到現在我還沒有看到它的錯誤。但是爲了記錄,我沒有寫這些代碼,我只是想測試它。再次感謝你! –

相關問題