《Effective java》读书记录-第8条-覆盖equals时需要遵守通用约定
来源:互联网 发布:淘宝运费模板删除不了 编辑:程序博客网 时间:2024/06/05 05:46
覆盖equals方法看起来似乎很简单,但是有许多覆盖方式会导致错误。
1.类的每个实例本质上都是唯一的。
2.不关心类是否提供了“逻辑相等(logical equality)”的测试功能。
3.超类已覆盖equals,从超类继承过来的行为对于子类也是合适的。
4.类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。
equals方法实现了等价关系(equvialence relation):
1.自反性(reflexive)。
对于任何非null的引用值x,x.equals(x)必须返回true。
下面的代码就违反了自反性,collection中的contains方法果断告诉你,明明存在的实例却不在collection中。
public class MyEquals { private String name; public MyEquals (String name) { this.name=name; } @Override public boolean equals(Object obj) { if(obj instanceof MyEquals){ return this.reflexivityEquals(this,(MyEquals) obj); } return false; } /** * 比较两个是否是一个对象 * 这是错误的比较方法,错误在匹配相同时也返回false */ private boolean reflexivityEquals(MyEquals x,MyEquals y){ if(x==y){ return false; }else { return false; } }}public class MyEqualsTest extends TestCase { public void testRelexivity(){ MyEquals myEquals1=new MyEquals("1"); Collection<myequals> cols=new ArrayList<myequals>(); cols.add(new MyEquals("2")); cols.add(new MyEquals("3")); cols.add(myEquals1); if(cols.contains(myEquals1)){ System.out.println("TRUE"); }else{ System.out.println("FALSE"); } }}</myequals></myequals>
2.对称性(symmetric)。
对于任何非null的引用值x和y,y.equals(x) 返回true,x.equals(y)也必须返回true 。
下面的例子就出现了非对称的错误。
public class MyEquals { private String name; public MyEquals (String name) { this.name=name; } @Override public boolean equals(Object myEquals) { if(obj instanceof MyEquals){ return this.symmtircEquals(this,(MyEquals) obj); } return false; } /** * * 比较时忽略大小写 * 这是错误的比较方法,错误就在只把x转成小写,没有考虑到y是大写的情况 */ private boolean symmtircEquals(MyEquals x,MyEquals y){ if(x.getName().toLowerCase().equals(y.getName())){ return true; }else { return false; } } public String getName() { return name; }}public class MyEqualsTest extends TestCase { public void testSymmetric(){ MyEquals x=new MyEquals("a"); MyEquals y=new MyEquals("A"); if(x.equals(y)){ System.out.println("x==y TRUE "); }else{ System.out.println("x==y FALSE "); } if(y.equals(x)){ System.out.println("y==x TRUE "); }else{ System.out.println("y==x FALSE "); } }}
3.传递性(transitive)。
对于任何非null的引用值x、y和z,x.equals(y) 返回true,y.equals(z)也返回true,那么x.equals(z)也必须返回true。
public class Point { private int x; private int y; public Point (int x,int y) { this.x=x; this.y=y; } @Override public boolean equals(Object obj) { if(obj instanceof Point){ return x==((Point)obj).getX() && y==((Point)obj).getY(); } return false; } public int getX() { return x; } public int getY() { return y; }}public class PointColor extends Point { private String color; public PointColor(int x, int y,String color) { super(x, y); this.color=color; } @Override public boolean equals(Object obj) { if (obj instanceof PointColor) return super.equals(obj) && ((PointColor) obj).getColor()==this.color; return false; } public String getColor() { return color; }}public class MyEqualsTest extends TestCase { public void testTransitivity(){ PointColor x=new PointColor(1,1,"1"); PointColor y=new PointColor(1,1,"1"); Point z=new Point(1,1); if(x.equals(y)){ System.out.println("x==y TRUE "); }else{ System.out.println("x==y FALSE "); } if(y.equals(z)){ System.out.println("y==z TRUE "); }else{ System.out.println("y==z FALSE "); } if(z.equals(x)){ System.out.println("z==x TRUE "); }else{ System.out.println("z==x FALSE "); } }}
4.一致性(consistent)。
对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
如果两个对象相等,它们就必须始终保持相等,除非它们中有一个对象(或者两个对象)被修改。换句话说,可变对象在不同时候可以与不同的对象相等,而不可变对象则不会这样(这里引出了一个不可变对象的概念,参考第15条)。 在编写一个类的时候,应该仔细考虑它是否应该是不可变的,如果认为它应该是不可变的,就必须保证equals方法满足这样的限制:相等的对象永远相等,不相等的对象永远不相等。
无论类是否是可变的,都不要使equals方法依赖于不可靠的资源。equals方法应该对驻留在内存中的对象执行确定的计算。java.net.URL的equals方法违反了equals约定,具体可以参看代码
public boolean equals(Object obj) { if (!(obj instanceof URL)) return false; URL u2 = (URL)obj; return handler.equals(this, u2); }
5.非空性(Non-nullity)。对于任何非null的引用值x,x.equals(null)必须返回false。
一般不需要写特定的代码去判断,因为 instanceof的第一个参数为null,那么instanceof操作符都指定返回false。
总结:
1.使用==操作符检查“参数是否为该对象的引用”。如果是,返回true。这样可以优化性能,减少执行。
2.使用instanceof操作符检查“参数是否为正确类型”。如果不是,返回false。
3.把参数转换成正确类型。因为转换之前进行过instanceof策四,所以可以确保成功。
4.对于该类的每个“关键(significant)”域,检查参数中域是否与该对象中对应的域匹配。
如果测试全部成功,返回true,否则返回false。
float和double这两个基本类型需要用Float.compare或Double.compare方法,其他的可以直接使用==操作符。
对于可以为null的域,为了避免NullPointException异常,习惯使用(field == null ? o.field==null :field.equals(o.field)),如果filed和o.filed通常是相同的对象引用,那么(field==o.field||(field != null && field.equals(o.field)))会更快。
域的比较顺序可能会影响到equals的性能,为了获得最佳性能,应该最先判断最可能不一致的域,或者开销最低的域。
5.当你编写完成了equals方法之后,应该问自己三个问题,它是否是对称的、传递的、一致的?
最后的一些告诫:
覆盖equals时总要覆盖hashCode。
不要企图让equals方法过于智能。
不要将equals声明中的Object对象替换为其他的类型。
- 《Effective java》读书记录-第8条-覆盖equals时需要遵守通用约定
- Effective Java 第8条:覆盖equals时要遵守通用约定
- effective java (8) 覆盖equals时请遵守通用约定
- 第8条:覆盖equals时请遵守通用约定
- 第8条:覆盖equals时请遵守通用约定
- 第8条:覆盖equals时请遵守通用约定
- 第8条:覆盖equals时请遵守通用约定
- Effective Java 第八条:覆盖equals请遵守通用约定
- Effective Java读书笔记-覆盖equals时遵守的通用约定
- effective java(8) 之覆盖equals时遵守通用的约定
- 第8条 对于所有对象都通用的方法——覆盖equals时请遵守通用约定
- 第八条:覆盖equals时请遵守通用约定
- 《Effective Java》读书笔记07--覆盖equals时要遵守通用约定
- 覆盖equals时请遵守通用约定——Effective Java 读书笔记
- 《Effective Java》读书笔记——覆盖equals时请遵守通用约定
- Effective Java(避免使用最终方法、覆盖equals时请遵守通用约定)
- 覆盖equals时遵守通用约定
- 覆盖equals时请遵守通用约定
- Python中os和shutil模块实用方法集锦
- Arduino PWM的探究
- Socket入门Demo——Tcp协议
- android glide的一些基本用法
- TCP 三次握手 四次挥手
- 《Effective java》读书记录-第8条-覆盖equals时需要遵守通用约定
- 局部图像特征描述概述
- C++补全计划1
- android集成开发环境搭建
- android DatePicker 点击文本框,禁止弹出键盘
- MySQL学习笔记——常用基础操作
- javascript限制复选框最多选中几项
- cmake安装步骤
- java虚拟机简介