《Effective Java》阅读笔记-第三章
Effective Java 阅读笔记
第三章 对于所有对象都通用的方法
第 10 条 重写 equals 时请遵守通用约定
重写 equals 方法很简单,但是很容易出现错误,最直接避免这种错误的方式就是不重写 equals,
当出现任意一下情况的时候,就不需要重写 equals:
- 类的每个实例在逻辑上就是唯一的
- 没比要提供“逻辑相等”功能的
- 父类的 equals 方法对子类也适用时
- 私有类,并且确定 equals 方法不会被调用
上述任一情况就可以不重写 equals 方法。
如果需要提供 equals 方法,那应该遵循一下约定:
- 自反性(reflexive):x 不为 null 时,
x.equals(x)
应当返回 true - 对称性(symmetric):x、y 不为 null 时,如果
x.equals(y)
为 true,那么y.equals(x)
也必须为 true - 传递性(transitive):x、y、z 不为 null 时,如果
x.equals(y) && y.equals(z)
为 true,那么x.equals(z)
也必须为 true - 一致性(consistent):如果
x.equals(y)
,并且x、y的值都没有修改,那么x.equals(y)
应始终为 true - 任何非 null 的引用x,
x.equals(null)
必须为 false
其他告诫:
- 重写 equals 时同时重写 hashCode 方法
- 不要让 equals 的实现过于繁重
- 不要修改 equals 参数的类型,也就是不要修改 Object 为具体的类
第 11 条 重写 equals 时必须重写 hashCode 方法
如果不重写 hashCode 方法,会导致 HashMap、HashSet等类无法正常运作。
下面是 Object 的规范:
- 如果对象没有改变,那么 HashCode对象的返回值应该是相同的
- 如果两个对象使用 equals 比较时相等了,那么 hashCode 方法必须返回相同的值
- 如果两个对象使用 equals 比较时不相等,那么 hashCode 方法返回值没有要求
第 12 条 尽量重写 toString 方法
可以方便 debug 等
第 13 条 谨慎重写 clone 方法
clone 方法在 Object 类上是 protected,实现了 Cloneable 接口的类并不一定具有能调用的 clone 方法.
Cloneable 接口只是标记该类可以被 clone,并且重写 clone 方法需要做的事情并不少,不仅需要逐一 clone 内部的引用类型,还需要调用 super.clone(),反正谨慎使用 clone 方法。
第 14 条 可比较对象可以实现 Comparable 接口
Comparable 接口中有一个返回值为 int 类型的单个方法:
public interface Comparable<T> {
public int compareTo(T o);
}
当前对象大于、等于、小于被比较的对象时,分别返回正数、0、负数。
实现接口时,有几点需要注意的:
由于 compareTo 方法只看正负,不看具体值,因此这里使用Math.signum()
来表示
- 实现必须保证
signum(x.compareTo(y)) == -signum(y.compareTo(x))
,即 x 向 y 比较,和 y 向 x 比较的意义是相同的。 - 比较关系必须可传递,即当
x.compareTo(y) && y.compareTo(z)
为 true 时,那么x.compareTo(z)
也一定为 true - 比较关系必须等价,即当
x.compareTo(y) == 0
时,那么任意 z 有:signum(x.compareTo(z)) == signum(y.compareTo(z))
- 强烈建议
(x.compareTo(y) == 0) == (x.equals(y))
,但是并非绝对必要,此时需要说明排序与 equals 不一致。
当违反前三条时,很有可能造成排序算法死循环(亲身经历)。
在 compareTo 方法中直接比较大于小于是非常繁琐的,可以使用 Java 8 新增的接口:Comparator
这里有一个比较的例子,根据实体类的字段进行比较:
@Getter
@Setter
@ToString
public static class MyEntity implements Comparable<MyEntity> {
private static final Comparator<MyEntity> COMPARATOR = Comparator.nullsLast(
Comparator.comparing(MyEntity::getName)
);
private String name;
@Override
public int compareTo(MyEntity o) {
return COMPARATOR.compare(this, o);
}
}
使用 Comparator 静态方法构造一个 Comparator 接口进行比较,会更加方便、易读(需要注意 null)。