Chapter 3 对于所有对象都通用的方法
第10条 覆盖equals
时请遵守通用约定
什么时候应该覆盖equals()
方法呢?
如果类具有逻辑相等的概念, 通常属于值类(value class)的情形.
例外: 实例受控的值类: 枚举, 一个值对应一个实例, 所以不需要覆盖equals
.
覆盖equals
方法的时候, 必须要遵守通用约定:
- 自反性(reflexive): 对象必须等于其自身.
- 对称性(symmetric): 任何两个对象关于它们是否相等的结果保持一致.
- 传递性(transitive): 如果一个对象等于第二个对象, 第二个对象等于第三个对象, 则第一个对象一定等于第三个对象.
- 一致性(consistent): 如果两个对象相等, 它们就必须始终保持相等, 除非它们被修改了.
- 非空性(non-nullity): 所有的对象都必须不等于null.
- 对于任何非null的引用值x,x.equals(null)必须返回false
第11条 覆盖equals
时总要覆盖hashCode
在每个覆盖了equals
方法的类中, 也必须覆盖hashCode
方法.
如果不这样做的话, 就会违反Object.hashCode
的通用约定, 从而导致该类无法结合所有基于散列的集合一起正常运作, 这样的集合包括HashMap
, HashSet
和Hashtable
.
通用约定:
- 程序执行期间, 只要对象的
equals
方法的比较操作所用到的信息没有被修改, 那么多次调用hashCode
方法都必须始终如一地返回同一个整数.
(在应用程序多次执行的过程中, 每次执行所返回的整数可以不一致.) - 如果两个对象根据
equals
比较相等, 那么hashCode
结果应该相同. - 如果两个对象根据
equals
比较不相等, 则hashCode
不一定要产生不同的整数结果.
(但是不相等的对象产生不同的hashCode有可能提高散列表的性能. 一个好的散列函数通常倾向于为不相等的对象产生不相等的散列码.)
第12条 始终要覆盖toString
提供好的toString
方法可以使类使用起来更加舒适, 更利于调试.
实践上, toString
方法应该返回对象中所有感兴趣的信息.
在实现toString
的时候, 必须要做出一个很重要的决定: 是否在文档中指定返回值的格式.
- 好处: 标准, 明确, 适合人阅读, 容易在对象和它的字符串表示法之间来回转换.
- 不足: 一旦指定, 就必须坚持这种格式, 如果要改变就会破坏原来的代码和数据.
无论是否指定了格式, 都应该在文档中说明意图.
第13条 谨慎地覆盖clone
来自Object规范中的clone
方法的通用约定:
创建和返回对象的一个拷贝. 这个拷贝的精确含义取决于该对象的类.
通常要求:
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
通常要求这三个表达式都为true, 但不是绝对.
第14条 考虑实现Comparable
接口
compareTo
方法是Comparable
接口中唯一的方法, 允许进行等同性和顺序比较:
将对象与指定的对象进行比较, 当该对象小于, 等于或大于指定对象的时候, 分别返回一个负整数, 零或正整数.
由compareTo
施加的等同性测试, 也一定遵守相同于equals
约定所施加的限制条件: 自反性, 对称性和传递性.
强烈建议(x.compareTo(y) == 0) == (x.equals(y))
.
Java 8提供了一些comparator构造的方法, 比如comparingInt
, thenComparingInt
, comparing
等, 可以链式组合使用.
由于compareTo
方法并没有指定返回值的大小, 而只是指定了符号, 所以可以利用这一点进行简化.
反例: 不要用两个数相减的方法: 注意可能会溢出导致错误, 并且这样做并没有明显的性能改善.
-> 推荐用静态的Integer.compare
方法或者comparingInt
来构造Comparator.