对所有对象都通用的方法
1. 覆盖 equals 请遵守通用规定
Object
提供的 equals()
方法通常是足够的,不要去覆写它,除非迫不得已。
覆盖 equals 的通用规定
如果类具有自己特有的 "逻辑相等" 概念,而且超类也没有覆盖 equals()
方法,那么这个类需要覆盖 equals()
方法。
在覆盖 equals()
方法时,(对于任何非空引用)需要遵守以下通用规定:
规范 | 解释 |
---|---|
自反性 | x.equals(x) 必须返回 true |
对称性 | x.equals(y) 为 true 时, y.equals(x) 也必须返回 true |
传递性 | x.equals(y) 为 true , y.equals(z) 为 true 时, x.equals(z) 也必须返回 true |
一致性 | 只要 x 与 y 没有改变,x.equals(y) 的返回值永远不会改变 |
实现高质量 equals 方法的诀窍
- 使用
==
操作符检查 “参数是否为这个对象的引用”。如果是,直接返回true
。 - 使用
instanceof
操作符检查 “参数是否为正确的类型” - 把参数转换成正确的类型
- 进行比较关键域时,优先比较最有可能不一致的域
- 覆盖
equals()
方法时,总要覆盖hashCode()
方法 - 不要企图让
equals()
方法过于智能(会更复杂更难满足规约) - 不要将
equals()
中声明的Object
对象替换为其他类型
java
//这个方法不能正常工作,因为没有覆盖 `Object` 的 `equals()` 方法
public boolean equals(MyClass obj) { ... }
关键域类型 | 比较方式 |
---|---|
float | Float.compare(float,float) 进行比较 |
double | Double.compare(double,double) 进行比较 |
其他基本类型 | 使用 == 操作符进行比较 |
引用类型 | 递归调用 equals() 方法进行比较 |
总结:不要轻易覆写 equals
; 如果需要,使用 Auto Value
框架或 IDE 生成的也优先于自己实现
2. 覆盖 equals 时总要覆盖 hashCode
Object 规范中要求,如果两个对象(equals
)相等,那么它们的 hashCode()
方法也必须返回相同的值。
如果覆盖了 equals
方法导致两个实例对象逻辑相等,但这两个实例对象的原始 hashCode
肯定是不相等的,所以需要重写 hashCode
方法。
同样的,建议使用 Auto Value
开源框架或 IDE 来重写 hashCode
方法。
3. 始终要覆盖 toString
Object
类提供的toString()
实现往往不是用户期望看到的,尽量覆盖更易于使用和调试。
4. 谨慎地覆盖 clone
除了数组之外,复制功能最好由构造器或工厂方法提供,而不是实现 Cloneable
接口的 clone()
方法
5. 考虑实现 Comparable 接口
compareTo()
方法并没有在 Object
类中声明,相反它是 Comparable
接口的唯一方法。
一旦类实现了 Comparable
接口,它就可以与许多泛型算法以及依赖于该接口的集合实现进行协作;事实上, jdk 的所有值类都实现了 Comparable
接口。
如果你正在编写一个值类,它具有非常明显的内在排序关系,那么就应该坚决考虑实现 Comparable
接口。
为 PhoneNumber 实现 Comparable 接口
java
final class PhoneNumber implements Comparable<PhoneNumber>{
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
//编写一个比较器
private static final Comparator<PhoneNumber> COMPARATOR =
//此次需要显示指定类型
Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparing(pn -> pn.prefix)
.thenComparing(pn -> pn.lineNum);
@Override
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this,pn);
}
}