標籤:
為什麼equals()方法要重寫?
判斷兩個對象在邏輯上是否相等,如根據類的成員變數來判斷兩個類的執行個體是否相等,而繼承Object中的equals方法只能判斷兩個引用變數是否是同一個對象。這樣我們往往需要重寫equals()方法。
我們向一個沒有重複對象的集合中添加元素時,集合中存放的往往是對象,我們需要先判斷集合中是否存在已知對象,這樣就必須重寫equals方法。
怎樣重寫equals()方法?
重寫equals方法的要求:
1、自反性:對於任何非Null 參考x,x.equals(x)應該返回true。
2、對稱性:對於任何引用x和y,如果x.equals(y)返回true,那麼y.equals(x)也應該返回true。
3、傳遞性:對於任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那麼x.equals(z)也應該返回true。
4、一致性:如果x和y引用的對象沒有發生變化,那麼反覆調用x.equals(y)應該返回同樣的結果。
5、非空性:對於任意非Null 參考x,x.equals(null)應該返回false。
1、自反性原則
在JavaBean中,經常會覆寫equals方法,從而根據實際業務情況來判斷兩個對象是否相等,比如我們寫一個person類,根據姓名來判斷兩個person類執行個體對象是否相等。代碼如下:
public class Person { private String name; public Person(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { if (obj instanceof Person) { Person person= (Person) obj; return name.equalsIgnoreCase(person.getName().trim()); } return false; } public static void main(String[] args){ Person p1=new Person("張三"); Person p2=new Person("張三 "); List<Person> list = new ArrayList<Person>(); list.add(p1); list.add(p2); System.out.println("是否包含張三:"+list.contains(p1)); System.out.println("是否包含張三:"+list.contains(p2)); }}
list中含有這個產生的person對象,結果應該為true,但是實際結果: 這裡考慮了字串空格的問題,去除前後的空格。
是否包含張三:true
是否包含張三:false
第二個為什麼會是false呢?原因在於list中檢查是否含有元素時是通過調用對象的equals方法來判斷的,也就是說 contains(p2)傳遞進去會依次執行p2.equals(p1)、p2.equals(p2),只要一個返回true,結果就是true。但是這裡p2.equals(p2)返回的是false?由於我們對字元前後進行了空格的切割造成p2.equals(p2)的比較實際上是:“張三 ”.equals(“張三”),一個有空格,一個沒有空格就出錯了。
這個違背了equals的自反性原則:對於任何非Null 參考x,x.equals(x)應該返回true。
這裡只要去掉trim方法就可以解決。
2、對稱性原則
上面這個例子,還並不是很好,如果我們傳入null值,會怎麼樣呢?增加一條語句:Person p2=new Person(null);
結果:
| 12 |
是否包含張三:trueException in thread "main" java.lang.NullPointerException |
原因在執行p2.equals(p1)時,由於p2的name是一個null值,所以調用name.equalsIgnoreCase()方法時就會報null 指標異常。
這是在覆寫equals方法時沒有遵循對稱性原則:對於任何應用x,y的情形,如果想x.equals(y)返回true,那麼y.equals(x),也應該返回true。
應該在equals方法裡加上是否為null值的判斷:
@Override public boolean equals(Object obj) { if (obj instanceof Person) { Person person= (Person) obj; if (person.getName() == null || name == null) { return false; }else{ return name.equalsIgnoreCase(person.getName()); } } return false; }
3、傳遞性原則
現在我們有一個Employee類繼承自person類:
public class Employee extends Person{ private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public Employee(String name,int id) { super(name); this.id = id; // TODO Auto-generated constructor stub } @Override public boolean equals(Object obj) { if(obj instanceof Employee){ Employee e = (Employee)obj; return super.equals(obj) && e.getId() == id; } return super.equals(obj); } public static void main(String[] args){ Employee e1=new Employee("張三",12); Employee e2=new Employee("張三",123); Person p1 = new Person("張三"); System.out.println(p1.equals(e1)); System.out.println(p1.equals(e2)); System.out.println(e1.equals(e2)); }}
只有在name和ID都相同的情況下才是同一個員工,避免同名同姓的。在main裡定義了,兩個員工和一個社會閑雜人員,雖然同名同姓但肯定不是同一個人。運行結果應該三個都是false才對。但是:
true
true
false
p1盡然等於e1,也等於e2,不是同一個類的執行個體也相等了?因為p1.equals(e1)是調用父類的equals方法進行判斷的它使用instanceof關鍵字檢查e1是否是person的執行個體,由於employee和person是繼承關係,結果就是true了。但是放過來就不成立,e1,e2就不等於p1,這也是違反對稱性原則的一個典型案例。
e1竟然不等於e2?e1.equals(e2)調用的是Employee的equals方法,不僅要判斷姓名相同還有判斷工號相同,兩者的工號不同,不相等時對的。但是p1等於e1,也等於e2,e1卻不等於e2,這裡就存在矛盾,等式不傳遞是因為違反了equals的傳遞性原則:對於執行個體對象x、y、z;如果x.equals(y)返回true,y.equals(z)返回true,那麼x.equals(z)也應該返回true。
上述情況會發生是因為父類使用instanceof關鍵字(是否是這個特定類或者是它的子類的一個執行個體),用來判斷是否是一個類的執行個體對象的,這很容易讓子類“鑽空子”。想要解決也很簡單,使用getClass進行類型的判斷,person類的equals方法修改如下:
@Override public boolean equals(Object obj) { if (obj != null && obj.getClass() == this.getClass()) { Person person= (Person) obj; if (person.getName() == null || name == null) { return false; }else{ return name.equalsIgnoreCase(person.getName()); } } return false; }
4、必須覆寫hashCode方法這樣結果就是三個false。
覆寫equals方法就必須覆寫hashCode方法,這是Javaer都知道的。原因就是HashMap的底層處理機制是以數組的方式儲存map條目的,這其中的關鍵是這個數組下標的處理機制:依據傳入元素的hashCode方法的傳回值決定其數組的下標,如果該數組位置上已經有了map條目,且與傳入的索引值相等則不處理,若不相等則覆蓋;如果數組位置沒有條目,則插入,並加入到map條目的鏈表中。同理檢查鍵是否存在也是根據雜湊嗎確定文職,然後遍曆尋找索引值的。
那麼對象的hashCode方法返回的是什麼呢?他是一個對象的雜湊碼,是有Object類的本地方法產生的,確保每個對象有一個雜湊碼。
具體的可以看另一篇博文:http://www.cnblogs.com/silence-hust/p/4510574.html
?
java重寫equals方法需要注意的幾點