第八条:覆盖equals时请遵守通用约定

==是物理相等  

equals是逻辑相等

因为每个类的实例对象本质上都是唯一的 ,利用物理相等(==)是指一个实例只能相等于它自己。

利用逻辑相等是(equals)指 一个实例是否和另一个实例的某些关键域相等,从而来判断这两实例是否相等。

Object类的equals方法的实现:物理相等的话就逻辑相等。

什么时候不需要覆盖Object类的equals方法?

1.希望这个类的实例只能和自身相等,不覆盖equals方法,只继承Object类的equals方法。

   我们比较这个类的实例对象是否相等,无论是使用 == 或者equals 实际上都是 进行物理相等的判断。

2.这个类的超类已经覆盖了Object类的equals方法,并且这个类继承超类的equals方法,对于它自身来说也是合适的,这时不覆盖equals方法。

  例如:Set实现类都继承了AbstractSet类的equals方法,List实现类都继承了AbstractList类的equals方法,Map实现类都继承了AbstractMap类

  的equals方法。

3.确定这个类的equals方法永远不会被调用时。

什么时候需要覆盖Object类的equals方法?

当一个类需要自己的特有的“逻辑相等”时,例如我们希望两个实例的某些关键域相等时,我们就认为它们两者是逻辑上相等的,调用equals方法就返回true。

如果不覆盖equals方法,继承Object类的equals方法,实际上进行的物理相等的判断,即一个实例只能和它自身相等。

例如:一些“值类”,如Integer,Date,String等,这些类我们在调用它们的equals方法时,希望只要两个实例的关键域相等,就返回true,而不是两个实例

是同一个实例时再返回true。而且这些类的equals方法必须被覆盖,从而在作为Map集合的key,或者Set集合的元素时出现符合预期的结果,因为它们的equals

方法会被调用来判断两个实例是否逻辑相等。

但是对于一些实例受控的类,如单例类,枚举类,这些类就不需要覆盖equals方法了。

正确覆盖equals方法,需要遵守它的通用约定:

1.自反性: x.equals(x) 必须返回true  。实例自身必然逻辑相等。

2.对称性: x.equals(y) 与 y.equals(x) 返回结果应该相同,同为true或者同为false。

3.传递性: x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)应该返回true。

4.一致性: 只要比较的实例对象的关键属性值没有改变 ,那么无论调用多少次equals方法返回的结果都应该相同,一致。

5.对于非null的x实例,x.equals(null) 永远返回false。

上面的五条约定,十分重要。有许多类,包括所有的集合类在内,都依赖于传递给它们的实例对象是否遵守了equals约定。

结合所有的约定需求,得到实现高质量equals方法的诀窍:

1.使用==操作符检查“参数对象的引用是否是调用对象”的相同引用。如果是,返回true。

2.使用instanceof操作符检查参数对象的类型是否正确。这里的类型正确,一般是指验证参数对象的类型是否是当前类。有些情况

  也可以在当前类实现的接口的其他实现类,之间进行比较。

3.把参数对象转化为正确的类型。因为已经经过instanceof类型检查过了,类型转化可以确保成功。

4.对于该类中的每个“关键”域,检查参数对象中域是否与当前对象中对应的域相匹配。

如果上面的测试全部成功,就返回true,否则就返回false。

其中对于既不是float也不是double类型的基本类型域,可以直接使用==操作符进行比较,对于float类型的域,使用Float.compare方法,对于

double类型的域,使用Double.compare方法。对于引用类型的域,调用引用类型的equals方法进行比较。

当一个类的equals方法完成时,应该明确:它是否是对称的,传递的,一致的? 应该写测试代码去进行测试。

还要铭记一下告诫:

1.覆盖equals方法时总要覆盖hashCode方法。(第九条)

2.不要企图让equals方法太过智能。

3.不要把参数Object类型 换成任何其他类型。因为Object类提供的equals方法的参数类型就是Object,如果把参数Object类型换成其他类型,

就变成了equals方法的重载,而不是equals方法的重写。  所以可以添加@Override注解 来避免这种错误。

原文地址:https://www.cnblogs.com/wangliyue/p/4448085.html