equals方法实现小记
4431 点击·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来实现,这个大家也不妨可以去看看里面的源码,最近时间不多,以后回来再看。。。。。。 | |
![]() | ![]() |