上善若水
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方法,他同时也列举了几种我们不需要实现equals方法的情况:
1)类的每个实例从本质上来说是唯一的,如Thread类的实例。
2)我们并不会用到该类的equals方法,如Random类,虽然可以比较两个Random的实例,以判断两个实例是否可以产生相同的随机数,设计者认为这样的需求用到的场合很少,因而就没有重写equals方法。
3)父类已经实现了equals方法,并且父类实现方式和子类实现方式是一样的,如大部分的Set实现的equals方法使用AbstractSet类提供的equals方法,List实现则使用AbstractList,Map实现使用AbstractMap的。
4)一个private类或package-private类,我们自己可以确保我们不会使用到它们的equals方法。
同时书也提出一般只有值类型的类才需要实现equals方法,像Date、Integer、Order(作为bean来使用)等。
另外,我们在实现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):多次调用x.equals(y)的结果应该是一样的。
5)对任何非null实例x,x.equals(null)==false。

根据这些特性,我们可以写出如下代码:
 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实现方法前面没有null、this的判断,因为instanceof可以解决null的问题,而super.equals()方法可以解决this问题,但是我还是喜欢把它们都分出来,这样写的更加明了一些。另外,事实上,这里的实现并没有遵循对称性的原则,因为如果A是B的子类,而这个equals方法在A类中,那么AInstance.equals(BInstance)==false,若B也实现了类似的equals方法,则BInstance.equals(AInstance)==true(当A没有新的比较字段时,或许这个时候A根本就不需要实现equals方法,如本文开头列出的第三条),这是因为AInstance instanceof BInstance == true,反之则为false。不过由于这种情况并不常见,所以就不去care了。:)

    事实上,这里我之所以要记录这些代码,主要是因为有ObjectUtils类的存在。记得以前在学C#的时候,它的Object类提供了一个静态的Equals方法,我一直对这个方法的存在感到很疑问,直到这次自己写这个equals方法才弄明白,因为虽然在equals方法实现中,最后还要判断类字段是否equals,然后这些字段都有可能是null的,如果没有提供这个静态的equals方法,我们就需要自己来判断每个字段是否为null,然后才可以调用它的equals方法,这样就比较麻烦了,而Object.Equals方法正是对这种行为的封装,我们只要使用一个方法就可以安全的实现类成员的equals。这也是我加ObjectUtils类的意义所在。希望以后能有机会向这个ObjectUtils类填充更多的实用方法。:)

PS:如一楼所说在commons中的EqualsBuilder已经实现了相同的功能,而且代码更加完善,有兴趣的可以看看那里的代码,我这里只是对这次新的的记录,代码只是对当前我考虑的场景中使用,并没有考虑其他方面。另外,在《Effective Java》中也是建议equals和hashCode两个方法应该是同时实现的,一楼也有说可以用HashCodeBuider来实现,这个大家也不妨可以去看看里面的源码,最近时间不多,以后回来再看。。。。。。
posted on 2011-06-29 19:05 DLevin 阅读(2429) 评论(10)  编辑  收藏 所属分类: Core Java

FeedBack:
# re: equals方法实现小记
2011-06-29 22:27 | Lancelot
你这么大费周章的写这么多代码是大可不必的。
不要写这个“ObjectUtils ”了,只要用commons的“EqualsBuilder”就好了。
而且,既然你重写了equals方法,那就最好也重写hashCode方法(也有工具:“HashCodeBuilder”)。

比你代码的通用性与可读性都要好得多。  回复  更多评论
  
# re: equals方法实现小记
2011-06-29 23:10 | dirzy
需求都不清晰就开始编码?。。。!!!
你觉得你有必要写这个ObjectUtils类嘛?重复造垃圾轮子!  回复  更多评论
  
# re: equals方法实现小记[未登录]
2011-06-29 23:30 | jim
“不要以为读过几本黑手党的书你就可以做黑社会老大,你试过被人用枪指着头嘛?”

不要以为读过几本稍微深入点的书,就开始想谈什么架构!代码都不多写的架构师,架构个毛啊!最鄙视代码都不写的所谓的“架构师”。

年轻人做技术要脚踏实地,话说的有些重。接受不了的,当我没说。。。  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 00:28 | DLevin
@Lancelot
从开始看Java开始,一直认为apache是一个伟大的组织,里面有很多我们能想得到的工具和框架。有打算以后花一段时间好好研究一下里面的代码。这里的代码只是对自己经历的一种记录,无他~~~
BTW:这两个方法我确实也是不知道的,学习了~~~~  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 00:33 | 过路客
18 if(obj instanceof Customer) {
19 return false;
20 }
这几句代码有问题有问题,另外还不明白你所说得架构师,现在架构也被滥用了么
  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 00:41 | DLevin
@jim
呵呵,虽然对第二段的表达方式不怎么赞同,但是你的观点我还是非常认同的,其实我从来没有认为我是一个架构师,虽然我一直在往这个方向努力,不过还有好长一段路要走,事实上,我现在都在避免谈论我之前的那段经历(这篇文章是没多想就写上了,呵呵)  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 00:55 | DLevin
@过路客
嗯,是写错了,多谢哈,架构那事就不用再提了,算我笔误,嘿嘿~~~  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 08:54 | 窝窝影视
架构 没啥关系  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 11:05 | Lancelot
@DLevin
EqualsBuilder/ HashCodeBuilder不是方法,都是工具类,commons里还有大量类工具类,如果是基本的底层功能你都可以从commons里面去翻翻看。

如果对commons的框架不太了解,可以去看看《Jakarta Commons Cookbook》(影印版),虽然版本老了些,但你仍会发现不少你未来可能会用到的工具的。  回复  更多评论
  
# re: equals方法实现小记
2011-06-30 22:18 | DLevin
@Lancelot
呵呵,见笑了,对commons里面的内容还真不了解,有计划要好好研究一下那里的内容,可惜最近一直没时间,多谢哈~~~~  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: