讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議45:覆寫equals方法時不要識別不出自己 >

建議45:覆寫equals方法時不要識別不出自己

我們在寫一個JavaBean時,經常會覆寫equals方法,其目的是根據業務規則判斷兩個對象是否相等,比如我們寫一個Person類,然後根據姓名判斷兩個實例對象是否相同,這在DAO(Data Access Objects)層是經常用到的。具體操作是先從數據庫中獲得兩個DTO(Data Transfer Object,數據傳輸對像),然後判斷它們是否是相等的,代碼如下:


class Person{

private String name;

public Person(String_name){

name=_name;

}

/*name的getter/setter方法省略*/

@Override

public boolean equals(Object obj){

if(obj instanceof Person){

Person p=(Person)obj;

return name.equalsIgnoreCase(p.getName().trim());

}

return false;

}

}


覆寫的equals做了多個校驗,考慮到從Web上傳遞過來的對象有可能輸入了前後空格,所以用trim方法剪切一下,看看代碼有沒有問題,我們寫一個main:


public static void main(Stringargs){

Person p1=new Person("張三");

Person p2=new Person("張三");

List<Person>l=new ArrayList<Person>();

l.add(p1);

l.add(p2);

System.out.println("列表中是否包含張三:"+l.contains(p1));

System.out.println("列表中是否包含張三:"+l.contains(p2));

}


上面的代碼產生了兩個Person對像(注意p2變量中的那個張三後面有一個空格),然後放到List中,最後判斷List是否包含了這兩個對象。看上去沒有問題,應該打印出兩個true才是,但是結果卻是:


列表中是否包含張三:true

列表中是否包含張三:false


剛剛放到list中的對象竟然說沒有,這太讓人失望了,原因何在呢?List類檢查是否包含元素時是通過調用對象的equals方法來判斷的,也就是說constains(p2)傳遞進去,會依次執行p2.equals(p1)、p2.equals(p2),只要有一個返回true,結果就是true,可惜的是比較結果都是false,那問題就出來了:難道p2.equals(p2)也為false不成?

還真說對了,p2.equals(p2)確實是false,看看我們的equals方法,它把第二個參數進行了剪切!也就是說比較的是如下等式:


"張三".equalsIgnoreCase("張三")


注意前面的「張三」是有空格的,那這個結果肯定是false了,錯誤也就此產生了。這是一個想做好事卻辦成了「壞事」的典型案例,它違背了equals方法的自反性原則:對於任何非空引用x, x.equals(x)應該返回true。

問題知道了,解決也非常容易,只要把trim()去掉即可,注意解決的只是當前問題,該equals方法還存在其他問題。