咱们知道Object类中全部的非final方法equals、hashcode、toString、clone、finalize都有明确的约定,由于这些方法就是设计用来被子类覆盖的。若是不能按照约定覆盖,那么其余依赖这些方法的的类就没法正常工做,好比HashMap和HashSet。java
咱们先来讨论什么状况下不用覆盖equals方法:数组
上述四种状况咱们能够不用考虑覆盖equals方法,但有些类不免仍是要覆写,让咱们先看看有哪些约定的内容:性能
对咱们来讲上面的每一条都是很简单的,甚至认为原本就该这样。但事实上当咱们要同时知足上述五条,有时候确是一个几乎不可能完成的任务。优化
自反性就是对于任何非null的引用值x,x.equals(x)必须返回true。这个应该很好理解,若是违反的话,最简单的你往集合里添加东西,而后调用contains,你会发现他会告诉你集合不包含你刚给添加的实例。.net
对于非空引用x,y,若是x.equals(y)返回true,那么y.equals(x)也必须返回true。简单来讲就是x等于y,那么y就要等于x。举个例子看看违反的状况:若是x是一个不去分大小写的自定义字符串类的一个实例,那么x假设为“hello”,y为普通字符串”Hello“那么x.equals(y)应该返回true(x是自定义类,equals方法中调用了equalsIgnoreCase)。可是y.equals(x)返回false。设计
如今咱们把x放到集合中,而后list.contains("Hello")会返回什么。true or false?code
true也好false也好,你把但愿都寄托在list.contains的内部实现上,内部遍历每一个元素时,是使用x.equals(y),仍是y.equals(x),并且这还会致使另外一个问题,及时如今能够正确运行,那么将来的实现改变了怎么办,你的代码就不受你控制了。对象
传递性就是x等于y,y等于z,那么x也会等于z。一样,咱们也举个例子来讲明这个问题。如今咱们有一个Point类,坐标系中的一个二维点,它的equals方法应该是比较该点的坐标(x,y),若是坐标相同则为同一个点。如今咱们想要表示一个有颜色的点,咱们继承了Point类,如今考虑equals方法。继承
首先父类的equals方法能用吗?答案很明显,不能。若是用父类的equals方法,那么颜色就不会比较,红点就会和绿点相同。因而咱们覆写equals方法,加上颜色比较,问题结束了吗?接口
若是咱们比较有色点和无色点会发生什么呢?父类会根据坐标判断是否相对,而子类只会返回false,由于缺乏颜色信息,这样会不知足对称性的要求。咱们再一次修正这个问题。咱们在子类equals内部先判断类型(instanceof),若是是和父类型Point比较则不考虑颜色信息,和同类型ColorPoint则考虑颜色。此次把问题解决了吗?
咱们再来考虑一种特殊状况,红点(ColorPoint),绿点(ColorPoint),点(Point)。假设三个点在同一个位置上,上面的方法咱们会的到红点等于点,点等于绿点,可是红点不等于绿点。你是否是发现了什么,是的,咱们又违反了传递性。
咱们还能够继续改进用getClass判断类型,让不一样类型比较都会返回false,即便是子类和父类比。可是这种方式也会带来一些麻烦,它会限制父类的覆用。
咱们改如何办呢?事实上这是面向对象语言中关于等价关系的一个基本问题:咱们没法在扩展可实例化的类的同时,既增长新的值组件,同时又保留equals约定,除非愿意放弃面向对象的抽象带来的优点。
在这种状况下通常使用组合,让ColorPoint持有Point是一种不错的选择,这种方法有点相似于getClass方法,但能够不用考虑继承带来的一些其余方面的反作用。
对于上述状况咱们使能够避免的,咱们能够把父类设计成接口或者抽象类,只要父类不和子类混用就不会出现问题。
简单来讲就是相等的对象永远相等,不相等的永远不相等。要想知足这一点只要在equals中不要用不可靠的资源就好了。例如,java.net.URL这个类的equals方法使用了IP地址,主机名会映射到ip,也就是随着时间的推移equals结果可能发送变化。
若是不处理null,那么equals里会抛出异常。