第7条】在改写equals的时候请遵守通用约定

    技术2022-05-19  29

    《第3章 对所有对象都通用的方法》

        这是我认为最有用的一章,真的该好好问问自己,这些最最基础的东西,自己都掌握吗?    Object是个即普通又特殊的类。说它普通是因为所有的类都是由它派生来的,说它特殊是因为它是所有其他类的父类必须先了解它才能写好你自己的类。    Object所有的非final方法,都是为改写(override)而设计的,改写方法都有明确的约定,这一章就是讲述该如何改写这些法方的。

     

    【第7条】在改写equals的时候请遵守通用约定

        equals即“相等”之意,看起来改写很容易,但是实际中却容易犯错误。equals的改写规范: 1)自反性:x.equals(x)一定为true 2)对称性:当且仅当x.dquals(y)为true;那么y.equals(x)也必须为true 3)传递性:如果x.equals(y)为true,y.equals(z);那么x.equals(x)也必须为true 4)一致性:对于任意引用值x和y,如果用于equals比较的对象信息没有被修改的话,那么多次调用x.dquals(y)返回的值是一致的 5)对于非空引用值x,x.equals(null)一定返回false     看上去这些约定都是废话,一个头脑没有问题的人,对于“相等”的理解,不会对以上五条产生任何疑问。但落实到代码上就不是这么简单了,在改写完equals方法后用一些测试代码来逐条对照检查一下是非常有必要的。   

        一旦违反了其中一条,后果就是不堪设想的。

        比如:自反性。如果违反了“x.equals(x)一定为true”这一条。当这样的类的一个实例被放入到一个集合(collection)中后,当你通过contains方法向集合询问是否含有此实例时,集合会告诉你“没有”。为什么呢?contains方法就是靠调用放入其中的对象的equals方法来判断是包含的(编写contains方法的人认为所有的类都该有一个满足约定的equals方法)。    写好equals方法的“处方”: 1)使用==操作符检查“实参是否为指向对象的一个引用”     if (o==this)        return true;

     2)使用instanceof操作符检查“实参是否为正确的类型”     if (!(o instanceof MyClass))        return false;

     3)把实参转为正确的类型     MyClass mo = (MyClass)o;

     4)对于该类中每一个“关键(significant)”域,检查实参中的域与当前对象中对应的域值是否匹配     if (!(this.field == null ? o.field == null : this.equals(o.field)))     //或者写成 if(!(this.field == o.field || (this.field != null && this.field.equals(o.field)))) 对于this.field和o.field通常是相同的对象引用,会更快一些。       return false;     //比较下一个field     //都比较完了     return true;

     5)最后还要确认以下事情   5.1)改写equals的同时,总是(必须)要改写hashCode方法(见【第8条】),这是极容易被忽略的,有极为重要的   5.2)不要企图让equals方法过于聪明   5.3)不要使用不可靠的资源。如依赖网络连接   5.4)不要将equals声明中的Object对象替换为其他类型。         public boolean equals(MyClass) 这样的声明并不鲜见,往外使程序员数小时搞不清楚为什么不能正常工作         原因是这里是重载(overload)而并不是改写(override)(或称为覆盖、重写)         相当于给equals又增加了一个实参类型为MyClass的重载,而实参为Object的那个equals并没有被改写,依然还是从Object继承来的最初的那个equals,所总是看不到程序员想要的效果。因为类库或其他人写的代码都是调用实参为Object型的那个equals方法的(别人又如何在事前知晓你今天写的MyClass呢?)

     


    最新回复(0)