java 存在可覆盖的调用_覆盖equals时请遵守通用约定(8)

1、覆盖equals方法需谨慎,可能会导致严重后果

最容易避免的方式就是,不去覆盖

2、覆盖equals 方法期望你满足:

(1)类的每个实例本质上都是唯一的

代表活动实体而不是值得类确实如此

Object 的equals 本质调用的是 ==

对于像String 类就是做值判断,和这里要求的唯一不是一种情况

9bed7cb140f41564e14ef515137fcb01.png

(2)不关心类是否提供了”逻辑相等“的测试功能

在类的每个实例都与自身相等情况下,从Object继承已经足够了

(3)超类已经覆盖了equals 方法,从超类继承的equals方法对于子类也是合适的

Set实现都从AbstractSet 继承equals 实现,Map、List 也是

(4)类是私有的 或包级私有的 可以保证equals 方法永远不会被调用

未声明(包级私有)、 public、protected、private

类的访问权限:

60e49d65595d7b34fdc768038a133657.png

要我们用代码来保证,以防其被以外调用

c7da32ea07999e27c48c469096679b43.png

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

57fb067d6b6c939eebf4343ad1bbaf7f.png

(3)传递性:任何非null 值,x.equals(y)==true,x.equals(z)==true,则必须满足x.equals(y)==true

我们无法扩展可实例化类的同时,既增加组件,又保留equals 方法的约定

ColorPoint 继承自Point ,多了一个color 属性

2b41d2e14d31b2941a030e39e5e981d3.png

覆盖equals 方法1:

该方法问题在于,无法比较 p.equals(cp)==true, cp.equals(p)==false 没有实现传递性

ed59ece60de01e9ea0ccce059668cfb3.png

覆盖equals 方法2:

instanceof具有传递性(实例 的类、父类、实现接口 都满足)

实现了对称性,没有实现传递性

a49ca0d1f2f16b2f96808ccb37dd109c.png

比如:

p1.equals(p2)==true,p2.equals(p3)==true, 但是:p1.equals(p3)==false

666d9c51f1d951ef9d707aa5e5ee4743.png

这里可以使用组合的方式满足上述需求:

dcafe3c08ba8dc9e7b7f2e2f250de73e.png

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

下述 判空是不必要的

72c0062c513bf3ed9443395a6c763fed.png

判空不必要原因如下,即类型判断的存在

如果 o 是空,instanceof 返回的都是false,所以不需要单独判空

567731ef7ecbb65ea9ebe720770094d2.png

有许多类,包括所有集合类,都依赖于传递给他们的equals 方法是否遵从约定

6、高质量equals 方法实现诀窍:

(1)使用== 检查 "参数是否为这个对象的引用"

仅仅是性能上的一种优化

(2)使用instanceof 检查 ”参数是否为正确的类型“

也可以是接口

(3)把参数转换成正确的类型

(4)检查该类中的每个”关键“域,检查参数中的域是否与该对象中对应的域相匹配

如果是接口,必须通过接口方法访问每一个域

类的话,根据可访问性访问

对于非(float、double)浮点的基本类型,可采用== 比较

对于浮点数,可采用Float.compare、Double.compare比较

浮点数特殊处理是必要的,因为存在Float.NaN、-0.0f等特殊常量(Double类似)

数组域,对每个元素比较;或者采用Arrays.equals方法比较

有些引用域null 是合法的,采用下述比较:

cb4e3ec4bcffca1851e7a541f004acc8.png

相同的对象引用,下述方法更快:

b909f93e2a76af110294582bae514a96.png

域的比较比等同比较复杂得多,可保存”范式“ 以尽量使用低成本精确比较

域的比较顺序也会影响性能

优先比较开销最低的域、最可能不一致的域,同时满足二者

(5)完成equals 方法,再三确认是否 对称、一致、传递

还有 自反性、非空性,这俩一般自动满足

覆盖equals  时,总要覆盖hashcode<9条>

不要企图让equals 过于智能 不要过度准求各种等价关系

如:File 类的指向同一个文件的各种符号链接相等,File 没有那么做

不要将equals 参数中的Object 替换成其他类型

如下所述,这样并没有覆盖Object的equals 方法,仅仅重载了equals 方法

这样在某些时候提升性能,但复杂度加大,得不偿失

fd21cffc727726abe7da7e83db3b33d3.png

使用@Override 编译器会告诉你问题

fa8cb4eac30df1f531448e756e84b284.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值