内容简介:不覆盖equals方法,类的每个实例都只与它自身相等。如果满足了以下任何一个条件,就正是所期望的结果:需要覆盖equals的情况:如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为。覆盖equals方法的时候需要遵守的通用约定(等价关系):
不覆盖equals方法,类的每个实例都只与它自身相等。如果满足了以下任何一个条件,就正是所期望的结果:
- 类的每个实例本质上都是唯一的。
- 不关心类是否提供了“逻辑相等”的测试功能。
- 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。
- 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。
需要覆盖equals的情况:如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为。
覆盖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.equals(null)必须返回false。
里氏替换原则:一个类型的任何重要属性也将适用于它的子类型,因此为该类型编写的任何方法,在它的子类型上也应该同样运行得很好。
结合这些要求,实现高质量equals方法的诀窍:
- 使用==操作符检查“参数是否为这个对象的引用”。如果是则返回true。这是一种性能优化,如果比较操作有可能更昂贵,就值得这么做。
- 使用instanceof操作符检查“参数是否为正确的类型”。如果不是,则返回false。“正确的类型”是指equals方法所在的那个类,有些情况下,是指该类所实现的某个接口。
- 把参数转换成正确的类型。因为转换之前进行过instanceof测试,所以确保会成功。
-
对于该类中的每个“关键域”,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部成功,则返回true,否则返回false;
- 对于既不是float也不是double类型的基本类型域,使用==操作符进行比较;
- 对于对象引用域,可以递归地调用equals方法;
- 对于float域,可以使用Float.compare方法;
- 对于double域,则使用Double.compare方法;对float和double域进行特殊的处理是必要的,因为存在着Float.NaN、-0.0f以及类似的double常量;
- 对于数组域,则要把以上这些知道原则应用到每个元素上。
- 编写完成了equals方法之后,要问三个问题:它是否是对称的、传递的、一致的?还要编写单元测试来检验这些特性。
还有一些告诫:
- 覆盖equals时总要覆盖hashCode;
- 不要企图让equals方法过于智能;
- 不要讲equals声明中的Object对象替换为其他的类型。
覆盖equals时总要覆盖hashCode
在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。
Object规范:
- 在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。
- 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
- 如果两个对象根据equals(Obejct)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生不同的整数结果。但是给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。
如果hashCode方法为不相等的对象产生了很多相等的散列码,那么散列码相等的这些对象都被映射到同一个散列桶中,会是散列表退化成链表,极大的影响了散列表的性能。
一个好的散列函数通常倾向于“为不相等的对象产生不相等的散列码”。
始终要覆盖toString
在实际应用中,toString方法应该返回对象中包含的所有值得关注的信息。无论是否制定输出的格式,都应该在文档中明确地表明你的意图。无论是否指定格式,都为toString返回值中包含的所有信息,提供一种编程式的访问途径。
谨慎地覆盖clone
Cloneable接口的目的是作为对象的一个mixin接口,表明这样的对象允许克隆。但是它缺少一个clone方法,Object的clone方法是受保护的。Cloneable接口决定了Object中受保护的clone方法实现的行为:如果一个类实现了Cloneable,Object的clone方法就返回该对象的逐域拷贝,否则就会抛出CloneNotSupportedException异常。
如果实现Cloneable接口是要对某个类起到作用,类和它的所有超类都必须遵守一个相当复杂、不可实施的,并且基本上没有文档说明的协议,由此得到一种语言之外的机制:无需调用构造器就可以创建对象。
拷贝对象往往会导致创建它的类的一个新实例,但它同时也会要求拷贝内部的数据结构,这个过程没有调用构造器。
如果你覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone而得到的对象。如果类的所有超类都遵守这条规则,那么调用super.clone最终会调用Object的clone方法,从而创建出正确类的实例。
实际上,clone方法就是另一个构造器;你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。
如果专门为了继承而去设计一个clone方法,那就应该模拟Object.clone的行为:它应该被声明为protected,抛出CloneNotSupportedException,并且该类不应该实现Cloneable接口。这样可以使子类具有实现或者不实现Cloneable接口的自由。还有,如果决定用线程安全的类实现Cloneable接口,那么要记得它的clone方法必须实现很好的同步。
Cloneable具有上述这么多的问题,可以肯定的说,其他的接口都不应该扩展这个接口,为了继承而设计的类也不应该实现这个接口,对于一个为了继承而设计的类,如果你未能提供行为良好的受保护的clone方法,它的子类就不可能实现Cloneable接口。
考虑实现Comparable接口
compareTo方法是Comparable接口中唯一的方法,compareTo方法不但允许进行简单的等同性比较,而且允许执行顺序比较。类实现了Comparable接口,就表明它的实例具有内在的 排序 关系。
一旦实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。Java平台类库中的所有值类都实现了Comparable接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母排序、按数值顺序或者按年代排序,那就应该坚决考虑实现这个接口:
public interface Comparable<T> {
int compareTo(T t);
}
就好像违反了hashCode约定的类会破坏其他依赖于散列做法的类一样,违反compareTo约定的类也会破坏其他依赖于比较关系的类。依赖于比较关系的类包括有序集合类TreeSet和TreeMap,以及 工具 类Collections和Arrays,他们内部包含有搜索和排序算法。
CompareTo方法中域的比较是顺序的比较,而不是等同性的比较。比较对象引用域可以使通过递归地调用compareTo方法来实现。如果一个域没有实现Comparable接口,或者你需要使用一个非标准的排序关系,就可以使用一个显式的Comparator来代替,或者编写自己的Comparator,或者使用已有的Comparator。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Realm of Racket
Matthias Felleisen、Conrad Barski M.D.、David Van Horn、Eight Students Northeastern University of / No Starch Press / 2013-6-25 / USD 39.95
Racket is the noble descendant of Lisp, a programming language renowned for its elegance and power. But while Racket retains the functional goodness of Lisp that makes programming purists drool, it wa......一起来看看 《Realm of Racket》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
URL 编码/解码
URL 编码/解码