上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0
              最近做項目,在一次寫equals方法時突然悟出了一些心得,小記之,以備后用。在《Effective Java(第二版)》的Item7中提出我們要盡量避免重新equals方法,他同時也列舉了幾種我們不需要實現(xiàn)equals方法的情況:
          1)類的每個實例從本質(zhì)上來說是唯一的,如Thread類的實例。
          2)我們并不會用到該類的equals方法,如Random類,雖然可以比較兩個Random的實例,以判斷兩個實例是否可以產(chǎn)生相同的隨機數(shù),設(shè)計者認(rèn)為這樣的需求用到的場合很少,因而就沒有重寫equals方法。
          3)父類已經(jīng)實現(xiàn)了equals方法,并且父類實現(xiàn)方式和子類實現(xiàn)方式是一樣的,如大部分的Set實現(xiàn)的equals方法使用AbstractSet類提供的equals方法,List實現(xiàn)則使用AbstractList,Map實現(xiàn)使用AbstractMap的。
          4)一個private類或package-private類,我們自己可以確保我們不會使用到它們的equals方法。
          同時書也提出一般只有值類型的類才需要實現(xiàn)equals方法,像Date、Integer、Order(作為bean來使用)等。
          另外,我們在實現(xiàn)equals方法是也要遵循以下幾個原則:
          1)自反性(reflexive):x.equals(x)==true
          2)對稱性(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)對任何非null實例x,x.equals(null)==false。

          根據(jù)這些特性,我們可以寫出如下代碼:
           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類的代碼如下:
           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》這本書中,貌似equals實現(xiàn)方法前面沒有null、this的判斷,因為instanceof可以解決null的問題,而super.equals()方法可以解決this問題,但是我還是喜歡把它們都分出來,這樣寫的更加明了一些。另外,事實上,這里的實現(xiàn)并沒有遵循對稱性的原則,因為如果A是B的子類,而這個equals方法在A類中,那么AInstance.equals(BInstance)==false,若B也實現(xiàn)了類似的equals方法,則BInstance.equals(AInstance)==true(當(dāng)A沒有新的比較字段時,或許這個時候A根本就不需要實現(xiàn)equals方法,如本文開頭列出的第三條),這是因為AInstance instanceof BInstance == true,反之則為false。不過由于這種情況并不常見,所以就不去care了。:)

              事實上,這里我之所以要記錄這些代碼,主要是因為有ObjectUtils類的存在。記得以前在學(xué)C#的時候,它的Object類提供了一個靜態(tài)的Equals方法,我一直對這個方法的存在感到很疑問,直到這次自己寫這個equals方法才弄明白,因為雖然在equals方法實現(xiàn)中,最后還要判斷類字段是否equals,然后這些字段都有可能是null的,如果沒有提供這個靜態(tài)的equals方法,我們就需要自己來判斷每個字段是否為null,然后才可以調(diào)用它的equals方法,這樣就比較麻煩了,而Object.Equals方法正是對這種行為的封裝,我們只要使用一個方法就可以安全的實現(xiàn)類成員的equals。這也是我加ObjectUtils類的意義所在。希望以后能有機會向這個ObjectUtils類填充更多的實用方法。:)

          PS:如一樓所說在commons中的EqualsBuilder已經(jīng)實現(xiàn)了相同的功能,而且代碼更加完善,有興趣的可以看看那里的代碼,我這里只是對這次新的的記錄,代碼只是對當(dāng)前我考慮的場景中使用,并沒有考慮其他方面。另外,在《Effective Java》中也是建議equals和hashCode兩個方法應(yīng)該是同時實現(xiàn)的,一樓也有說可以用HashCodeBuider來實現(xiàn),這個大家也不妨可以去看看里面的源碼,最近時間不多,以后回來再看。。。。。。
          posted on 2011-06-29 19:05 DLevin 閱讀(2737) 評論(10)  編輯  收藏 所屬分類: Core Java

          FeedBack:
          # re: equals方法實現(xiàn)小記
          2011-06-29 22:27 | Lancelot
          你這么大費周章的寫這么多代碼是大可不必的。
          不要寫這個“ObjectUtils ”了,只要用commons的“EqualsBuilder”就好了。
          而且,既然你重寫了equals方法,那就最好也重寫hashCode方法(也有工具:“HashCodeBuilder”)。

          比你代碼的通用性與可讀性都要好得多。  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-29 23:10 | dirzy
          需求都不清晰就開始編碼?。。。?。?!
          你覺得你有必要寫這個ObjectUtils類嘛?重復(fù)造垃圾輪子!  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記[未登錄]
          2011-06-29 23:30 | jim
          “不要以為讀過幾本黑手黨的書你就可以做黑社會老大,你試過被人用槍指著頭嘛?”

          不要以為讀過幾本稍微深入點的書,就開始想談什么架構(gòu)!代碼都不多寫的架構(gòu)師,架構(gòu)個毛啊!最鄙視代碼都不寫的所謂的“架構(gòu)師”。

          年輕人做技術(shù)要腳踏實地,話說的有些重。接受不了的,當(dāng)我沒說。。。  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 00:28 | DLevin
          @Lancelot
          從開始看Java開始,一直認(rèn)為apache是一個偉大的組織,里面有很多我們能想得到的工具和框架。有打算以后花一段時間好好研究一下里面的代碼。這里的代碼只是對自己經(jīng)歷的一種記錄,無他~~~
          BTW:這兩個方法我確實也是不知道的,學(xué)習(xí)了~~~~  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 00:33 | 過路客
          18 if(obj instanceof Customer) {
          19 return false;
          20 }
          這幾句代碼有問題有問題,另外還不明白你所說得架構(gòu)師,現(xiàn)在架構(gòu)也被濫用了么
            回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 00:41 | DLevin
          @jim
          呵呵,雖然對第二段的表達方式不怎么贊同,但是你的觀點我還是非常認(rèn)同的,其實我從來沒有認(rèn)為我是一個架構(gòu)師,雖然我一直在往這個方向努力,不過還有好長一段路要走,事實上,我現(xiàn)在都在避免談?wù)撐抑暗哪嵌谓?jīng)歷(這篇文章是沒多想就寫上了,呵呵)  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 00:55 | DLevin
          @過路客
          嗯,是寫錯了,多謝哈,架構(gòu)那事就不用再提了,算我筆誤,嘿嘿~~~  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 08:54 | 窩窩影視
          架構(gòu) 沒啥關(guān)系  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 11:05 | Lancelot
          @DLevin
          EqualsBuilder/ HashCodeBuilder不是方法,都是工具類,commons里還有大量類工具類,如果是基本的底層功能你都可以從commons里面去翻翻看。

          如果對commons的框架不太了解,可以去看看《Jakarta Commons Cookbook》(影印版),雖然版本老了些,但你仍會發(fā)現(xiàn)不少你未來可能會用到的工具的。  回復(fù)  更多評論
            
          # re: equals方法實現(xiàn)小記
          2011-06-30 22:18 | DLevin
          @Lancelot
          呵呵,見笑了,對commons里面的內(nèi)容還真不了解,有計劃要好好研究一下那里的內(nèi)容,可惜最近一直沒時間,多謝哈~~~~  回復(fù)  更多評論
            
          主站蜘蛛池模板: 大宁县| 云浮市| 图们市| 芮城县| 江门市| 开封市| 当涂县| 金阳县| 灵璧县| 洛川县| 安庆市| 商丘市| 阳曲县| 平湖市| 咸阳市| 宿州市| 武城县| 台中县| 广南县| 时尚| 长海县| 郴州市| 宁国市| 盱眙县| 九江县| 阿拉善左旗| 美姑县| 利津县| 海安县| 汾阳市| 修文县| 奈曼旗| 乌兰浩特市| 越西县| 嘉定区| 纳雍县| 西丰县| 云浮市| 克拉玛依市| 凤庆县| 凤城市|