遵守equals和hashCode方法的通用约定 (1)

覆盖equals方法和hashCode方法看似简单,但其实不然,如果没有按照jdk的通用规范去覆盖,那么基于这些约定的类将可能无法正常工作,例如基于散列的集合类HashMap和HashSet.
对于值类,我们通常需要覆盖Object.equals方法,因为我们希望通过equals方法知道它们在逻辑上是否相等.相应的这个类的实例可以被用作map的key,或者set的元素的时候才会表现出预期的行为. 对于"值类",枚举是个例外,因为枚举的每个值都是个单例.
在覆盖equals时,必须遵守JavaSE Object的规范:自反性(reflective), 对称性 (symmetric),传递性(transitive),一致性(consistent),对于任何非null的引用x, x.equals(null)返回false. 对于每个覆盖类equals的类,都应该有相应的单元测试去检查是否有没有违反上述约定。下面是个简单的例子:

public class PhoneNumber {

private int countryCode;
private String nationalNumber;

public PhoneNumber(){
super();
}

public PhoneNumber(int countryCode, String nationalNumber) {
super();
this.countryCode = countryCode;
this.nationalNumber = nationalNumber;
}
/**
* @return the countryCode
*/
public int getCountryCode() {
return countryCode;
}
/**
* @param countryCode the countryCode to set
*/
public void setCountryCode(int countryCode) {
this.countryCode = countryCode;
}

/**
* @return the nationalNumber
*/
public String getNationalNumber() {
return nationalNumber;
}
/**
* @param nationalNumber the nationalNumber to set
*/
public void setNationalNumber(String nationalNumber) {
this.nationalNumber = nationalNumber;
}
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(!(o instanceof PhoneNumber)){
return false;
}
PhoneNumber pn = (PhoneNumber) o;
return this.countryCode == pn.getCountryCode()
&& ( this.nationalNumber == null ? pn.nationalNumber == null : this.nationalNumber.equals(pn.nationalNumber));
}
}

import static org.junit.Assert.*;

import org.junit.Test;

public class PhoneNumberTest {

@Test
public void testEqualsReflexive(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
assertTrue(pn1.equals(pn1));
PhoneNumber pn2 = new PhoneNumber();
assertTrue(pn2.equals(pn2));
}

@Test
public void testEqualsSymmetric(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
PhoneNumber pn2 = new PhoneNumber(86, "12345");
assertEquals(pn1.equals(pn2), pn2.equals(pn1));
}


@Test
public void testEqualsTransitive(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
PhoneNumber pn2 = new PhoneNumber(86, "12345");
PhoneNumber pn3 = new PhoneNumber(86, new String("12345"));
assertTrue(pn1.equals(pn2));
assertTrue(pn2.equals(pn3));
assertTrue(pn1.equals(pn3));
}

@Test
public void testEqualsConsistent(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
PhoneNumber pn2 = new PhoneNumber(86, "12345");
for(int i=0; i<10 ; i++){
assertTrue(pn1.equals(pn2));
}
}

@Test
public void testEqualsWithNull(){
PhoneNumber pn1 = new PhoneNumber(86, "12345");
assertFalse(pn1.equals(null));
}

}


当然还有一些实现高质量equals方法的诀窍:
1. 使用==操作符检查"参数是否为正确的引用"
2. 使用instanceof检查类型
3. 把参数转化为正确的类型
4. 选择逻辑比较的关键域,注意比较的顺序,primitive的比较可以放在前面,或者最有可能不一致性的域
5. 如果有double,float类型,用Double.compare,Float.compare比较
6. 覆盖equals重要覆盖hashCode
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值