1)類(lèi)的每個(gè)實(shí)例從本質(zhì)上來(lái)說(shuō)是唯一的,如Thread類(lèi)的實(shí)例。
2)我們并不會(huì)用到該類(lèi)的equals方法,如Random類(lèi),雖然可以比較兩個(gè)Random的實(shí)例,以判斷兩個(gè)實(shí)例是否可以產(chǎn)生相同的隨機(jī)數(shù),設(shè)計(jì)者認(rèn)為這樣的需求用到的場(chǎng)合很少,因而就沒(méi)有重寫(xiě)equals方法。
3)父類(lèi)已經(jīng)實(shí)現(xiàn)了equals方法,并且父類(lèi)實(shí)現(xiàn)方式和子類(lèi)實(shí)現(xiàn)方式是一樣的,如大部分的Set實(shí)現(xiàn)的equals方法使用AbstractSet類(lèi)提供的equals方法,List實(shí)現(xiàn)則使用AbstractList,Map實(shí)現(xiàn)使用AbstractMap的。
4)一個(gè)private類(lèi)或package-private類(lèi),我們自己可以確保我們不會(huì)使用到它們的equals方法。
同時(shí)書(shū)也提出一般只有值類(lèi)型的類(lèi)才需要實(shí)現(xiàn)equals方法,像Date、Integer、Order(作為bean來(lái)使用)等。
另外,我們?cè)趯?shí)現(xiàn)equals方法是也要遵循以下幾個(gè)原則:
1)自反性(reflexive):x.equals(x)==true
2)對(duì)稱(chēng)性(symmetric):x.equals(y)==y.equals(x)
3)傳遞性(transitive):若x.equals(y)==true, y.equals(z)==true,則x.equals(z)==true。
4)一致性(consistent):多次調(diào)用x.equals(y)的結(jié)果應(yīng)該是一樣的。
5)對(duì)任何非null實(shí)例x,x.equals(null)==false。
根據(jù)這些特性,我們可以寫(xiě)出如下代碼:
1 public class Customer implements Serializable {
2 private static final long serialVersionUID = 1L;
3
4 private String id;
5 private String name;
6 private String role;
7
8 @Override
9 public boolean equals(Object obj) {
10 if(obj == null) {
11 return false;
12 }
13
14 if(this == obj) {
15 return true;
16 }
17
18 if(!(obj instanceof Customer)) {
19 return false;
20 }
21
22 Customer other = (Customer)obj;
23 return (ObjectUtils.equals(id, other.id) &&
24 ObjectUtils.equals(name, other.name) &&
25 ObjectUtils.equals(role, other.role));
26 }
27
28 public String getId() {
29 return id;
30 }
31 public void setId(String id) {
32 this.id = id;
33 }
34 public String getName() {
35 return name;
36 }
37 public void setName(String name) {
38 this.name = name;
39 }
40 public String getRole() {
41 return role;
42 }
43 public void setRole(String role) {
44 this.role = role;
45 }
46 }
其中ObjectUtils類(lèi)的代碼如下:2 private static final long serialVersionUID = 1L;
3
4 private String id;
5 private String name;
6 private String role;
7
8 @Override
9 public boolean equals(Object obj) {
10 if(obj == null) {
11 return false;
12 }
13
14 if(this == obj) {
15 return true;
16 }
17
18 if(!(obj instanceof Customer)) {
19 return false;
20 }
21
22 Customer other = (Customer)obj;
23 return (ObjectUtils.equals(id, other.id) &&
24 ObjectUtils.equals(name, other.name) &&
25 ObjectUtils.equals(role, other.role));
26 }
27
28 public String getId() {
29 return id;
30 }
31 public void setId(String id) {
32 this.id = id;
33 }
34 public String getName() {
35 return name;
36 }
37 public void setName(String name) {
38 this.name = name;
39 }
40 public String getRole() {
41 return role;
42 }
43 public void setRole(String role) {
44 this.role = role;
45 }
46 }
1 public class ObjectUtils {
2
3 /**
4 * Compare whether the left and right is equals
5 * It has already considered the null case
6 *
7 * @param left
8 * @param right
9 * @return
10 */
11 public static boolean equals(Object left, Object right) {
12 if(left == null && right == null) {
13 return true;
14 }
15 if(left == null && right != null) {
16 return false;
17 }
18 return left.equals(right);
19 }
20 }
在《Effective Java》這本書(shū)中,貌似equals實(shí)現(xiàn)方法前面沒(méi)有null、this的判斷,因?yàn)閕nstanceof可以解決null的問(wèn)題,而super.equals()方法可以解決this問(wèn)題,但是我還是喜歡把它們都分出來(lái),這樣寫(xiě)的更加明了一些。另外,事實(shí)上,這里的實(shí)現(xiàn)并沒(méi)有遵循對(duì)稱(chēng)性的原則,因?yàn)槿绻鸄是B的子類(lèi),而這個(gè)equals方法在A類(lèi)中,那么AInstance.equals(BInstance)==false,若B也實(shí)現(xiàn)了類(lèi)似的equals方法,則BInstance.equals(AInstance)==true(當(dāng)A沒(méi)有新的比較字段時(shí),或許這個(gè)時(shí)候A根本就不需要實(shí)現(xiàn)equals方法,如本文開(kāi)頭列出的第三條),這是因?yàn)锳Instance instanceof BInstance == true,反之則為false。不過(guò)由于這種情況并不常見(jiàn),所以就不去care了。:)2
3 /**
4 * Compare whether the left and right is equals
5 * It has already considered the null case
6 *
7 * @param left
8 * @param right
9 * @return
10 */
11 public static boolean equals(Object left, Object right) {
12 if(left == null && right == null) {
13 return true;
14 }
15 if(left == null && right != null) {
16 return false;
17 }
18 return left.equals(right);
19 }
20 }
事實(shí)上,這里我之所以要記錄這些代碼,主要是因?yàn)橛蠴bjectUtils類(lèi)的存在。記得以前在學(xué)C#的時(shí)候,它的Object類(lèi)提供了一個(gè)靜態(tài)的Equals方法,我一直對(duì)這個(gè)方法的存在感到很疑問(wèn),直到這次自己寫(xiě)這個(gè)equals方法才弄明白,因?yàn)殡m然在equals方法實(shí)現(xiàn)中,最后還要判斷類(lèi)字段是否equals,然后這些字段都有可能是null的,如果沒(méi)有提供這個(gè)靜態(tài)的equals方法,我們就需要自己來(lái)判斷每個(gè)字段是否為null,然后才可以調(diào)用它的equals方法,這樣就比較麻煩了,而Object.Equals方法正是對(duì)這種行為的封裝,我們只要使用一個(gè)方法就可以安全的實(shí)現(xiàn)類(lèi)成員的equals。這也是我加ObjectUtils類(lèi)的意義所在。希望以后能有機(jī)會(huì)向這個(gè)ObjectUtils類(lèi)填充更多的實(shí)用方法。:)
PS:如一樓所說(shuō)在commons中的EqualsBuilder已經(jīng)實(shí)現(xiàn)了相同的功能,而且代碼更加完善,有興趣的可以看看那里的代碼,我這里只是對(duì)這次新的的記錄,代碼只是對(duì)當(dāng)前我考慮的場(chǎng)景中使用,并沒(méi)有考慮其他方面。另外,在《Effective Java》中也是建議equals和hashCode兩個(gè)方法應(yīng)該是同時(shí)實(shí)現(xiàn)的,一樓也有說(shuō)可以用HashCodeBuider來(lái)實(shí)現(xiàn),這個(gè)大家也不妨可以去看看里面的源碼,最近時(shí)間不多,以后回來(lái)再看。。。。。。