第三章 对所有对象都通用的方法
第八条:覆盖equals时请遵守通用约定
在以下情况下,不需要覆盖equals:
- 类的每个实例本质上都是唯一的:即代表实体的类而不是值的类,比如Thread
- 不关心类是否提供了“逻辑相等”的测试功能:如Random
- 超类已经覆盖了equals,并且从超类继承过来的行为是合理的:比如HashMap从AbstractMap继承的equals。
- 类是私有的,确定equals不会被调用
当类具有自己特定的逻辑相等时,需要覆盖equals,这是需要遵守以下约定:
- 自反性:对于任何非null的x,x.equals(x)
- 对称性:x.equals(y) 等价于 y.equals(x)
- 传递性:x.euqals(y),y.equals(z)推出x.equals(z)
- 一致性:在x,y均未改变的情况下,多次调用x.equals(y)结果相同
- 非空性:任何x!=null,满足x.equals(null)=false
这些要求最容易被破坏的情况其实是比较的时候x,y不是同一个类的情况(有可能x是y的子类这种),所以要非常注意跨类的equals
实现高质量equals的诀窍:
- 使用==操作检查参数是否为这个对象的引用,如果是则返回true。这种情况可以优化性能
- 使用instanceof操作符检查参数是否为正确类型
- 把参数转换成正确类型。因为2检查过,所以不会有错
- 对于类中的每个关键域,检查参数中的域是否与对象中对应域匹配。
- 完成后检查是否满足对称性,传递性和一致性
另外一些诀窍:
- 覆盖equals总是要覆盖hashCode
- 不要让equals过于智能
- 不要将equals的参数从object变为其他类型。在覆盖equals时加上Override注解可以防止这种错误。
第九条:覆盖equals时总要覆盖hashcode
hashcode的约定内容如下:
- 对象的equals方法用到的域没有修改,hashcode方法必须始终如一的返回同一个整数
- 两个对象equals方法相等,则两个对象的hashcode结果也要相等
- 一个好的hashcode倾向于为不equals的对象产生不同的hash编码
计算hashcode的诀窍在于:
- 对equals用到的所有域做计算,同时排除equals没用到的域。虽然只对equals用到的部分域做计算能提高性能,但是这个结果可能带来更多的hash冲突
- 如果一个类是不可变的,同时计算hashcode开销比较大,可以考虑把hashcode存起来并且做延迟加载
第十条:始终要覆盖tostring
这一条没有上两条那么严格,但是也是一个好习惯。并且注意tostraing时的格式设计。因为print函数会隐式的调用tostring,你设计的格式会影响到后续的输出处理log处理等。最好在toString时有文档。
第十一条:谨慎的覆盖clone
默认情况下,clone是浅拷贝。参考文献
|
|
person.clone后name是同一个:
另外自己试了一下,对于list拷贝,遍历循环复制,使用List实现类的拷贝构造方法,使用list.addAll()方法,使用System.arraycopy()方法,使用collections.copy都是浅拷贝,使用序列化方法是相对靠谱的深拷贝。参考文献
clone函数是一个非常容易出错的函数。一般来讲,实现了Cloneable接口的类都应该用一个公有方法覆盖clone。此方法调用super.clone,然后修正任何需要修正的域。在对线程安全的类进行拷贝时记得同步。
其实可以用其他两种方法实现类似功能:
- 拷贝构造器:
public Sth (Sth sth)
- 拷贝工厂:
public static Sth newInstanc(Sth sth)
第十二条:考虑实现Comparable接口
- 确保所有的x、y满足 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。
- 确保比较关系是可传递的 x.compareTo(y)>0,y.compareTo(z)>0,那么x.compareTo(z)>0 。
- 若x.compareTo(y)==0, 则确保 sgn(z.compareTo(x)) == sgn(z.compareTo(y))。
- 建议(x.compareTo(y) == 0) == x.equals(y)。
依赖于比较关系的类包括:有序集合类 TreeSet和TreeMap 、工具类 Collections和Arrays,违反了compareTo方法约定会破坏这些依赖于比较关系的类 他们内部含有搜索和排序算法。类似于hashCode 跟hashMap的关系
Compareable接口是参数化的,因此不必进行类型检查也不需要对参数进行类型转换。
本文采用创作共用保留署名-非商业-禁止演绎4.0国际许可证,欢迎转载,但转载请注明来自http://thousandhu.github.io,并保持转载后文章内容的完整。本人保留所有版权相关权利。
本文链接:http://thousandhu.github.io/2016/10/02/第三章-对所有对象都通用的方法-effective-java/