在單元測試中面氓,當期望結果(Expect)和實際結果(Actual)都是對象時,進行Assert.assertEquals判斷時衫画,可能會不符合預期睡榆,我們認為符合預期的測試結果還是失敗场航,
主要還是對象equals比較不等導致的缠导。
原因分析
如果選用junit框架,當調用Assert.assertEquals(expected, actual)溉痢,最終會
調用對象當equals方法:
private static boolean isEquals(Object expected, Object actual) {
return expected.equals(actual);
}
我們知道僻造,Object是所有類的父類憋他,其equals方法實現(xiàn)
public boolean equals(Object obj) {
return (this == obj);
}
這就引出另一個問題,equals如何比較髓削,equals和=有何不同竹挡?
this 和 obj都是對象的引用,也就是對于任何非空的參考值x和y 立膛,當且僅當x和y引用相同的對象( x == y具有值true )時揪罕,該方法返回true 。
解決方法
重寫對象的equals方法宝泵。參見jdk官方文檔好啰,為了避免重寫equals方法引入該對象的其它操作漏洞,一定要實現(xiàn)等價關系儿奶。
public boolean equals(Object obj)
equals方法在非空對象引用上實現(xiàn)等價關系:
自反性 :對于任何非空的參考值x 框往,x.equals(x)應該返回true 。
對稱性 :對于任何非空引用值x和y 闯捎, x.equals(y)應該返回true當且僅當y.equals(x)回報true 椰弊。
傳遞性 :對于任何非空引用值x , y和z 隙券,如果x.equals(y)回報true個y.equals(z)回報true 男应,然后x.equals(z)應該返回true 。
一致性 :對于任何非空引用值x和y 娱仔,多次調用x.equals(y)始終返回true或始終返回false ,沒有設置中使用的信息equals比較上的對象被修改游桩。
對于任何非空的參考值x 牲迫, x.equals(null)應該返回false 。
請注意借卧,無論何時覆蓋該方法盹憎,通常需要覆蓋hashCode方法,以便維護hashCode方法的通用合同铐刘,該方法規(guī)定相等的對象必須具有相等的哈希碼陪每。
hashCode的總合同是:
只要在執(zhí)行Java應用程序時多次在同一個對象上調用該方法, hashCode方法必須始終返回相同的整數(shù)镰吵,前提是修改了對象中equals比較中的信息檩禾。 該整數(shù)不需要從一個應用程序的執(zhí)行到相同應用程序的另一個執(zhí)行保持一致。
如果根據equals(Object)方法兩個對象相等疤祭,則在兩個對象中的每個對象上調用hashCode方法必須產生相同的整數(shù)結果盼产。
不要求如果兩個對象根據equals(java.lang.Object)方法不相等,那么在兩個對象中的每個對象上調用hashCode方法必須產生不同的整數(shù)結果勺馆。 但是戏售,程序員應該意識到侨核,為不等對象生成不同的整數(shù)結果可能會提高哈希表的性能。
盡可能多的合理實用灌灾,由類別Object定義的hashCode方法確實為不同對象返回不同的整數(shù)搓译。 (這通常通過將對象的內部地址轉換為整數(shù)來實現(xiàn),但Java的編程語言不需要此實現(xiàn)技術锋喜。)
哈希沖突是所有哈希函數(shù)無法完全避免的侥衬,只能盡量的減少沖突。所以也無法保證對于不同的對象跑芳,其hashCode完全不同轴总。
具體實現(xiàn)
@Builder @Setter @Getter
public static class Tag extends Object {
private Integer id;
private String name;
private float confidence;
@Override
public int hashCode() {
return Objects.hash(this.id, this.name, this.confidence);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
Tag tag = (Tag) obj;
if (tag.getId() == this.id
&& tag.getName().equals(this.name)
&& tag.getConfidence() == this.confidence) {
return true;
}
return false;
}