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

来源:互联网 发布:淘宝转化手机端链接 编辑:程序博客网 时间:2024/06/05 20:39

第8条:覆盖equals时请遵守通用约定


覆盖equals方法看起来似乎很简单,但是有许多覆盖方式会导致错误,并且后果非常严重。最容易避免这类问题的办法就是不覆盖equals方法,在这种情况下,

类的每个实例都只与它自身相等。如果满足了以下任何一个条件,这就正是所期望的结果:

类的每个实例本质上都是唯一的。对于代表活动实体而不是值(value)的类来说确实如此,例如Thread。Object提供的equals实现对于这些类来说正是正确的行为。

不关心是否提供了“逻辑相等”的测试功能。例如,java.util.Random覆盖了equals,以检查两个Random实例是否产生相同的随机数序列,但是设计者并不认为客户

需要或者期望这样的功能。在这样的情况下,从Object继承得到的equals实现已经足够了。

超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。例如,大多数的Set实现都是从AbstractSet继承equals实现,List实现从AbstractList继承

equals实现,Map实现从AbstractMap继承equals实现。

类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。

在覆盖equals方法的时候,你必须要遵守它的通用约定。下面是约定的内容,来自Object的规范:

equals方法实现了等价关系:

自反性。对于任何非null的引用值x,x.equals(x)必须返回true。

对称性。对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。

传递性。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。

一致性。对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。

对于任何非null的引用值x,x.equals(null)必须返回false。

结合所有这些要求,得出了以下实现高质量equals方法的诀窍。

1.使用==操作符检查“参数是否为这个对象的引用”。

2.使用instanceof操作符检查“参数是否为正确的类型”。

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

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

5.当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?

覆盖equals时总要覆盖hashCode

不要企图让equals方法过于只能

不要将equals声明中的Object对象替换为其他的类型


第9条:覆盖equals时总要覆盖hashCode


一个很常见的错误根源在于没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做的话,就会违反

Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable。

下面是约定的内容,摘自Object规范:

在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一

地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。

如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。但是

程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。


第10条:始终要覆盖toString


第11条:谨慎地覆盖clone


Cloneable接口的目的是作为对象的一个mixin接口,表明这样的对象允许克隆(clone)。遗憾的是,它并没有成功地达到这个目的,其主要的缺陷在于,

它缺少一个clone方法,Object的clone方法是受保护的。如果不借助于反射,就不能仅仅因为一个对象实现了Cloneable,就可以调用clone方法。

即使是反反射调用也可能会失败,因为不能保证该对象一定具有可访问的clone方法。尽管存在这样那样的缺陷,这项设施仍然被广泛地使用着,因此

值得我们进一步地了解。

既然Cloneable并没有包含任何方法,那么它到底有什么作用呢?它决定了Object中受保护的clone方法实现的行为:如果一个类实现了Cloneable,Object

的clone方法就返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常。这是接口的一种极端非典型的用法,也不值得仿效。通常情况下,

实现接口是为了表明类可以它的客户做些什么。然而,对于Cloneable接口,它改变了超类中受保护的方法的行为。

如果实现Cloneable接口是要对某个类起到作用,类和它的所有超类都必须遵守一个相当复杂的、不可实施的,并且基本上没有文档说明的协议。由此得到一种

语言之外的机制:无需调用构造器就可以创建对象。


第12条:考虑实现Comparable接口

0 0
原创粉丝点击