當符合以下條件時不需要重寫equals方法:
1. 一個類的每一個實例本質上都是唯一的。
2. 不關心一個類是否提供了“邏輯相等”的測試功能
3. 超類已經改寫了equals方法,並且從超類繼承過來的行為對於子類也是合適的。
4. 一個類時私有的或者是package私有的,並且可以確定它的equals方法永遠不會被調用。(這種情況下最好將equals方法改寫成以下方式:
public boolean equals(Object obj){
throws new UnsupportOperationException();
}
只有當一個類有自己特定的“邏輯相等”概念,而且超類也沒有改寫equals以實現期望的行為,我們需要改寫equals方法。通常適用於“值類”。
在改寫equals方法時,也要遵守他們的通用約定(equals方法實現了等價關系):
1. 自反性:x.equals(x) = true;
2. 對稱性:如果有x.equals(y) = true,那么一定有y.equals(x) = true;
3. 傳遞性:對任意的x,y,z。如果有x.equals(y) = y.equals(z) = true,那么一定有x.equals(z)= true;
4. 一致性:無論多少次調用,x.equals(y)總會返回相同的結果。
5. 非空性(暫定):所有的對象都必須!=null;
上面的只是理論性的說法,更加具體的做法如下:
1. 使用==操作符檢查“實參是否為指向對象的一個引用”,如果是則返回true;
2. 使用instanceof操作符檢查“實參是否為正確的類型”,如果不是,則返回false;
3. 將實參裝換為正確的類型;
4. 對於該類中的每一個關鍵域,檢查實參中的域與當前對象中對應的域是否匹配。如果所有測試都成功,則返回true,否則返回false。
5. 方法完成之后,確定equals方法的對稱性,傳遞性,一致性。
一些忠告:
1.改寫equals方法的時候,必須改寫hashCode方法;
2.不要把equals聲明中的Object對象替換為其他類型;
改寫的形式必須為:public boolean equals(Object obj){...code segment...}
改寫equals時總要改寫hashCode
hashCode的通用約定如下:
1. 只要對象equals方法涉及到的關鍵域內容不改變,那么這個對象的hashCode總是返回相同的整數。(如果關鍵域內容改變,則hashCode返回的整數就可以改變)。
2. 如果兩個對象的equals(Object obj)方法時相等的,那么調用這兩個對象中的任意一個對象的hashCode方法必須產生相同的整數結果。如果兩個對象equals方法不同,那么必定返回不同的hashCode整數結果。(簡而言之:相等的對象必須有相等的散列碼即hashCode);
一個好的hashCode方法趨向於“為不相等的對象產生不相等的散列碼”理想情況下的散列函數應該把一個集合中不相等的實例均勻分布到所有可能的散列值上。下面給出一種參考方法:
1. 把某個非零常數值保存在一個叫做result的int類型的變量中
2. 為該對象中的每一個關鍵域f計算int類型的散列碼。
a) 為該域計算int類型的散列碼c:
i.如果域是Boolean類型,計算:(f?0:1)
ii.如果是byte,char,short,int類型,計算:(int)f
iii.如果是long類型,計算:(int)(f^(f>>32))
iv.如果是float類型,計算:Float.floatToIntBits(f)
v.如果是double類型,計算Double.doubleToLongBits(f)得到long類型的值,在按照long值對待,繼續進一步計算
vi.如果是對象引用,遞歸調用hashCode方法計算,如果遇到為null的關鍵域,則返回0
vii.如果是數組,將每一個元素都當做單獨的域來計算,遞歸應用上述規則
b) 按照下面公式,將a得到的散列碼c組合到result中
result = 37*result + c;
3. 返回result
4. 寫完之后,檢查hashCode方法是否相等的實例具有相等的散列碼,並找出錯誤原因。