10.1 不要轻易覆盖equals方法,除非迫不得已。因为:
10.1.1 类的每个实例本质上都是唯一的。
10.1.2 类没有必要提供”逻辑相等“的测试功能。
10.1.3 超类已经覆盖了equals,超类的行为对于这个类也是合适的。
10.1.4 类是私有的,或者是包级私有的,可以确定它的equals方法永远不会被调用。
10.2 如果要覆盖equals方法,必须遵守5个通用约定:
10.2.1 自反性(reflexive):x!=null && x.equals(x)==true
10.2.2 对称性(symmetric):x!=null && y!=null && x.equals(y)==true && y.equals(x)==true
10.2.3 传递性(transitive):x!=null && y!=null && z!=null && x.equals(y)==true && y.equals(z)==true, then x.equals(z)==true
10.2.4 一致性(consistent):x!=null && y!=null,多次调用x.equals(y)的返回值固定不变
10.2.5 (非空性):x!=null && x.equals(null)==false
一旦违反了约定,当其它对象面对你的对象时,完全不知道这些对象的行为会怎么样!
10.3 实现equals方法的几个诀窍:
10.3.1 使用==操作符检查”参数是否为这个对象的引用“
10.3.2 使用instanceof 操作符检查”参数是否为正确的类型“
10.3.3 把参数转换成正确的类型
10.3.4 对于该类中的每个”关键“域,检查参数中的域是否与该对象中的域相匹配
10.3.5 完成equals方法后,编写单元测试来检验5个约定,特别是对称性、传递性和一致性
10.3.6 覆盖equals方法时总要覆盖hashCode
10.3.7 不要企图让equals方法过于智能
10.3.8 不要将equals声明中的Object对象替换为其它的类型(实际上并未覆盖Object.equals)
若不覆盖hashCode,这个类无法和HashMap\HashSet一起正常工作。
相等的对象必须具有相等的hash code。
习惯上使用素数来计算散列结果,31可以用移位和减法来代替乘法。
不要试图从散列码计算中排除掉一个对象的关键域来提高性能。
不要对hashCode方法的返回值做出具体的规定。
提供好的toString实现可以使类用起来更加舒适,系统也更易于调试。
toString方法应该返回对象中包含的所有值得关注的信息,给返回格式添加注释。
应该为toString返回值中包含的所有信息提供可以通过编程访问的方法。
不可变的类永远都不应该提供clone方法。
clone方法就是另一个构造器,必须确保它不会伤害到原始的对象,并确保正确地被克隆对象中的约束条件。
对象拷贝的最好方法是提供一个拷贝构造器或拷贝工厂。但如果是数组,最好利用clone方法拷贝数组。
实现一个对排序敏感的类时,应该实现Comparable接口,这样可以与许多泛型算法协作。
在compareTo方法中比较域值时,应避免使用<和>操作符(繁琐且容易出错),而应该在装箱基本类型的类中使用静态的compare方法,或者在Comparator接口中使用比较器构造方法。
通用约定:
14.1 对称性:sgn(x.compareTo(y) == -sgn(y.compareTo(x), 或都抛出异常
14.2 传递性:x.compareTo(y)>0 && y.compareTo(z)>0,then x.compareTo(z)>0
14.3 x.compareTo(y)==0, then sgn(x.compareTo(z) == sgn(y.compareTo(z)
14.4 强烈建议 (x.compareTo(y)==0) == (x.equals(y))