你所忽略的,覆盖equals时需要注意的事项《effective java》
我们都知道Object的equals的比较其实就是==的比较,其实是内存中的存放地址的比较。正常逻辑上:类的每个实例本质上都是唯一的。
在工作中我们实际的业务逻辑往往有可能出现一些相对特殊的需求需要对equals方法进行重写,那么重写equals需要注意哪些规则或者通用的约定呢?
equals
方法实现了等价关系(equivalence relation):
- 自反性(reflexive)。对于任何非
null
的引用值x
,x.equals(x)
必须返回true
。 - 对称性(symmetric)。对于任何非
null
的引用值x
和y
,当且仅当y.equals(x)
返回true
时,x.equals(y)
必须返回true
。 - 传递性(transitive)。对于任何非
null
的引用值x
、y
和z
。如果x.equals(y)
返回true
,并且y.equals(z)
也返回true
,那么x.equals(z)
也必须返回true
。 - 一致性(consistent)。对于任何非
null
的引用值x
和y
,只要equals
的比较操作在对象中所用的信息没有被修改,多次调用x.equals(x)
就会一致地返回true
,或者一致的返回false
。 - 对于任何非
null
的引用值x
,x.equals(null)
必须返回false
。
第一点自反性不需要多说,基本上不可能会出现违背这条约定的情况当自己和自己比较的时候返回false。
第二点对称性,这种情况还是有可能会出现的。我们可以假设一个场景,这个场景是我们创建一个类并且里面只有一个String属性字段,这个类需要实现的是可以不区分字符串的大小写。
pubilc final class IgnoreCaseString {private final String s;public IgnoreCaseString(String s) {if (s == null)throw new NullPointerException();this.s = s;}@Overridepublic boolean equals(Object o) {if (o instanceof IgnoreCaseString)return s.equalsIgnoreCase(((IgnoreCaseString) o).s);if (o instanceof String)return s.equalsIgnoreCase((String) o);return false;} ...//更多代码(重写equals就需要重写hashCode) }
在这个类中,equals
方法的意图非常好,它的企图是可以与普通的字符串对象进行互操作。但是这段代码无意中触犯了对称性这个约定,从new IgnoreCaseString(“Po”).equals("po")是为true的,但是反过来“po”.equals(new IgnoreCaseString("Po"))的结果是false。假如违反了这一情况而没有去更正,他会破坏已有的集合框架的一些方法,使其变的不在准确。
IgnoreCaseString ics = new IgnoreCaseString("Po");List<IgnoreCaseString> list = new ArrayList<IgnoreCaseString>();list.add(ics);System.out.println(list.contains("po"));List<String> list1 = new ArrayList<>();list1.add("po");System.out.println(list1.contains(ics));
结果是,此时list.contains(s)会返回什么结果呢?没人知道,在Sun的当前实现中,它碰巧返回false,但这只是这个特定实现得出的结果而已。在其他的实现中,它有可能返回true(如上面代码中的list1.contains(ics)),或者抛出一个运行时(runtime)异常。一旦违反了equals约定,当其他对象面对你的对象时,你完全不知道这些对象的行为会这么样。
第三点传递性,equals约定:如果一个对象等于第二个对象,并且第二个对象又等于第三个对象,则第一个对象一定等于第三个对象。无意识违反这一情况其实不难想象,考虑子类的情形,子类增加的信息会影响到equals的比较结果。
public class TwoDCoordinate {private final int x;private final int y;public TwoDCoordinate(int x, int y) {this.x = x;this.y = y;}@Overridepublic boolean equals(Object o) {if (!(o instanceof TwoDCoordinate))return false;TwoDCoordinate p = (TwoDCoordinate)o;return p.x == x && p.y == y;} ...//更多代码(重写equals就需要重写hashCode)
} public class ThreeDCoordinate extends TwoDCoordinate{ private final int z; public ThreeDCoordinate(int x, int y, int z) { super(x, y); this.z=z; } @Override public boolean equals(Object o) { if (!(o instanceof TwoDCoordinate)) return false; if (!(o instanceof ThreeDCoordinate)) return o.equals(this); return super.equals(o) && this.z == ((ThreeDCoordinate) o).z; } ...//更多代码(重写equals就需要重写hashCode)
}
ThreeDCoordinate t1 = new ThreeDCoordinate(1, 2, 3);TwoDCoordinate t2 = new TwoDCoordinate(1, 2);ThreeDCoordinate t3 = new ThreeDCoordinate(1, 2, 4);System.out.println(t1.equals(t2));System.out.println(t2.equals(t1));System.out.println(t2.equals(t3));System.out.println(t1.equals(t3));
结果是true true true false,上述代码已经满足了自反性和对称性的约定,但是没有满足传递性,t1.equals(t2)为true,t2.equals(t3)为true,t1.equals(t3)却为false。
这种情况很多程序员会犯。
想要避免这种情况,其实可以用getClass测试代替instanceof测试。如将TwoDCoordinate 的equals 方法改为
public boolean equals(Object o) {if (o == null || o.getClass() != this.getClass())return false;TwoDCoordinate p = (TwoDCoordinate)o;return p.x == x && p.y == y;}
这种替代方式其实不会太糟糕,但是结果却不会太理想,暂时没有想到令人满意的办法实现既可以扩展又不可实例化的类,但是可以考虑复合优先于继承。
第四点一致性,equals约定的第四个要求是,如果两个对象相等,它们就必须始终保持相等,除非它们中有一个对象(或者两个都)被修改了。换句话说,可变对象在不同的时候可以与不同的对象相等,而不可变对象则不会这样。当你在写一个类的时候,应该仔细考虑她是否应该是不可变的。如果认为它应该是不可变的,就必须保证equals方法满足这样的限制条件:相等的对象永远相等,不相等的对象永远不相等。
转载于:https://www.cnblogs.com/saoyou/p/10318517.html
你所忽略的,覆盖equals时需要注意的事项《effective java》相关推荐
- java延迟覆盖_高效Java第九条覆盖equals时总要覆盖hashCode
原标题:高效Java第九条覆盖equals时总要覆盖hashCode 高效Java第九条覆盖equals时总要覆盖hashCode 在每个覆盖了equals方法的类中,也必须覆盖hashCode方法. ...
- Effective Java 对于所用对象都通用的方法 8.覆盖equals时请遵守通用约定.txt
对于eclipse覆盖equals方法就是Alt+Shift+S,而AS就是Alt+Insert.覆盖很简单,可是却容易导致错误,而且后果很严重.最容易避免的方法就是不覆盖,这样类就只与他自身相等. ...
- 第9条:覆盖equals时总要覆盖hashCode
在每个覆盖equals方法的类中,也必须覆盖hashCode方法.否则,会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常工作,包括HashMap,Hash ...
- java代码优化equal_java代码优化——覆盖equals时总要覆盖hashCode
HashCode规范 一个很常见的错误根源在于没有覆盖hashCode方法.在每个覆盖了equals方法的类中,也必须覆盖hashCode方法.如果不这样的话,就会违反Object.hashCode的 ...
- Effective Java之覆盖equal时要遵守通用约定(八)
先介绍一下Object的equal作用,==代表物理上的相等,equal代表逻辑上的相等,Object的equal的方法其实等同于==,这是因为它的逻辑是"如果对象物理相等,那么它们就逻辑相 ...
- 面试官:重写 equals 时为什么一定要重写 hashCode?
作者 | 磊哥 来源 | Java面试真题解析(ID:aimianshi666) 转载请联系授权(微信ID:GG_Stone) 重要说明:本篇为博主<面试题精选-基础篇>系列中的一篇,关注 ...
- 重写equals()时为什么也得重写hashCode()之深度解读equals方法与hashCode方法渊源
重写equals()时为什么也得重写hashCode()之深度解读equals方法与hashCode方法渊源 在使用Map接口时,我们的愿望是当key1.equals(key2)时,它们获取的valu ...
- nginx启动时指定prefix(覆盖编译时的 --prefix)
文章目录 nginx启动时指定prefix(覆盖编译时的 --prefix) nginx启动时指定prefix(覆盖编译时的 --prefix) -? 或者 -h:打印命令行参数帮助信息 -c fil ...
- 企业WiFi覆盖找时讯无线
现在人们的日常生活中是离不开无线网络的,一个企业更是离不开无线网络,日常的办公因为有WiFi的存在更加方便有效率,企业WiFi覆盖是十分重要的,而且信号要强才行,那就找时讯无线! 企业WiFi覆盖 企 ...
最新文章
- h264中profile和level的含义
- Python中使用数据库SQLite
- hdu 1495 非常可乐(BFS)
- What is the difference between “def” and “val” to define a function
- P5934-[清华集训2012]最小生成树【最小割】
- [渝粤教育] 西南科技大学 管理学原理 在线考试复习资料(5)
- openssl-1.0.0b - libssl 移植到ARM Linux
- 浅谈对象生存期与内存管理(转)
- Java--Java版本和JDK版本
- 在二维码中间添加logo或者图片
- 浪潮服务器安装ESXI6.7 无raid驱动解决方案
- 【CVPR2022】Beyond Fixation: Dynamic Window Visual Transformer
- 项目管理工具之Kanban
- 懒出天际--语音鼠标,解放双手,靠嘴使唤鼠标。SAPI语音识别,WINAPI鼠标消息
- 基于百度飞浆平台(EasyDL)设计的人脸识别考勤系统
- android sim卡工具,手机sim卡工具包老是弹出来怎么办?sim卡工具包不断弹出删除方法...
- 广西大学考计算机可以拿创新学分吗,广西大学创新实践学分实施办法
- 魔力宝贝 6.0 linux 一键端,魔力宝贝单机版6.0下载_魔力宝贝单机版下载-游戏下载...
- 十大经典Java手机游戏 Top Ten Best Java Mobile Games
- 1的取反为什么是-2
热门文章
- Linux SD卡驱动开发(六) —— SD卡启动过程总体分析
- Linux 系统应用编程——网络编程(TCP 协议三次握手过程)
- makefile中的patsubst, wildcard, notdir
- Go在谷歌:以软件工程为目的的语言设计
- 纯虚函数--抽象类中的this到底指的是哪个子类实例呢
- linux中追踪函数backtrace调用堆栈
- c#按ESC退出 或者接受其他键盘消息
- ie浏览器跨域报错问题;Access-Control-Allow-Headers 列表不存在请求表头 content-type;XMLHTTPRequest:网络错误 0x80070005,拒绝访问。
- [react] 为什么React并不推荐我们优先考虑使用Context?
- 前端学习(3250):dom的diff算法