1、覆盖equals方法需谨慎,可能会导致严重后果
最容易避免的方式就是,不去覆盖
2、覆盖equals 方法期望你满足:
(1)类的每个实例本质上都是唯一的
代表活动实体而不是值得类确实如此
Object 的equals 本质调用的是 ==
对于像String 类就是做值判断,和这里要求的唯一不是一种情况
(2)不关心类是否提供了”逻辑相等“的测试功能
在类的每个实例都与自身相等情况下,从Object继承已经足够了
(3)超类已经覆盖了equals 方法,从超类继承的equals方法对于子类也是合适的
Set实现都从AbstractSet 继承equals 实现,Map、List 也是
(4)类是私有的 或包级私有的 可以保证equals 方法永远不会被调用
未声明(包级私有)、 public、protected、private
类的访问权限:
要我们用代码来保证,以防其被以外调用
3、什么时候需要覆盖equals 方法:
类具有自己独有的逻辑相等概念(不同于对象等同概念)
父类又没有实现期望的 equals 行为
通常指 ”值类“ 如String、Integer、Date等
覆盖equals 方法,还需要使类的实例可以做map的key ,集合(set)的相关要求
上述map的key 要求不相等,集合(set)不能有重复项:都是equals判断的
实际上,上述先用hashcode判断,然后再equals 比较,提高效率
4、实例受控<1条>确保每个值最多存在一个对象的情况,不用覆盖
比如:枚举类型(逻辑相等等同于对象等同)
5、覆盖equals 方法,遵守通用约定:
(1)自反性:任何非null 值,x.equals(x) 必须返回true
(2)对称性:任何非null 值,x.equals(y)==true,则必须满足y.equals(x)==true
如下红框所示,该类实例和String 实例比较时
caseIns.equals(str)==true,但是 str.equals(caseIns)==false
(3)传递性:任何非null 值,x.equals(y)==true,x.equals(z)==true,则必须满足x.equals(y)==true
我们无法扩展可实例化类的同时,既增加组件,又保留equals 方法的约定
ColorPoint 继承自Point ,多了一个color 属性
覆盖equals 方法1:
该方法问题在于,无法比较 p.equals(cp)==true, cp.equals(p)==false 没有实现传递性
覆盖equals 方法2:
instanceof具有传递性(实例 的类、父类、实现接口 都满足)
实现了对称性,没有实现传递性
比如:
p1.equals(p2)==true,p2.equals(p3)==true, 但是:p1.equals(p3)==false
这里可以使用组合的方式满足上述需求:
java.sql.Timestamp 扩展了java.util.Date 增加了域,不满足equals 约定,是不好的案例
但是,我们可以在抽象类的子类中添加新的组件、并且不违反equals 约定
(4)一致性;任何非null 值,只要equals 比较所用信息没变,多次调用 都能得到 x.equals(y)==true
equals 方法永远不要依赖不可靠资源
java.net.URL的equals 方法依赖于主机IP地址的比较,IP地址是不靠谱资源,因为IP地址会变
(5)非空性:对于任何非null 值,必有 x.equals(null)==false
下述 判空是不必要的
判空不必要原因如下,即类型判断的存在
如果 o 是空,instanceof 返回的都是false,所以不需要单独判空
有许多类,包括所有集合类,都依赖于传递给他们的equals 方法是否遵从约定
6、高质量equals 方法实现诀窍:
(1)使用== 检查 "参数是否为这个对象的引用"
仅仅是性能上的一种优化
(2)使用instanceof 检查 ”参数是否为正确的类型“
也可以是接口
(3)把参数转换成正确的类型
(4)检查该类中的每个”关键“域,检查参数中的域是否与该对象中对应的域相匹配
如果是接口,必须通过接口方法访问每一个域
类的话,根据可访问性访问
对于非(float、double)浮点的基本类型,可采用== 比较
对于浮点数,可采用Float.compare、Double.compare比较
浮点数特殊处理是必要的,因为存在Float.NaN、-0.0f等特殊常量(Double类似)
数组域,对每个元素比较;或者采用Arrays.equals方法比较
有些引用域null 是合法的,采用下述比较:
相同的对象引用,下述方法更快:
域的比较比等同比较复杂得多,可保存”范式“ 以尽量使用低成本精确比较
域的比较顺序也会影响性能
优先比较开销最低的域、最可能不一致的域,同时满足二者
(5)完成equals 方法,再三确认是否 对称、一致、传递
还有 自反性、非空性,这俩一般自动满足
覆盖equals 时,总要覆盖hashcode<9条>
不要企图让equals 过于智能 不要过度准求各种等价关系
如:File 类的指向同一个文件的各种符号链接相等,File 没有那么做
不要将equals 参数中的Object 替换成其他类型
如下所述,这样并没有覆盖Object的equals 方法,仅仅重载了equals 方法
这样在某些时候提升性能,但复杂度加大,得不偿失
使用@Override 编译器会告诉你问题