读《Effective Java》笔记 - 条目10
条目10:在重写equals方法时要遵守通用约定
为什么需要重写 equals
方法?
- 对象比较的需求: 默认情况下,
Object
类的equals
方法是通过对象引用进行比较的,即只有当两个对象是同一个实例时才返回true
。这对于逻辑上“值相等”的比较(value equality)是不够的。例如,String
类重写了equals
方法,比较的是字符串内容而不是引用。 - 需要自定义比较逻辑: 在某些情况下,类需要基于特定的属性定义两个对象的相等性。例如,在一个
Person
类中,可以通过name
和id
来判断两个Person
对象是否相等。
通用约定
- 自反性(Reflexive):
- 对于任何非空引用值
x
,x.equals(x)
必须返回true
。
- 对于任何非空引用值
- 对称性(Symmetric):
- 对于任何非空引用值
x
和y
,如果x.equals(y)
返回true
,那么y.equals(x)
也必须返回true
。
- 对于任何非空引用值
- 传递性(Transitive):
- 对于任何非空引用值
x
、y
和z
,如果x.equals(y)
返回true
,并且y.equals(z)
返回true
,那么x.equals(z)
必须返回true
。
- 对于任何非空引用值
- 一致性(Consistent):
- 对于任何非空引用值
x
和y
,在对象未被修改的情况下,多次调用x.equals(y)
必须始终返回相同的结果。
- 对于任何非空引用值
- 非空性(Non-nullity):
- 对于任何非空引用值
x
,x.equals(null)
必须返回false
。
- 对于任何非空引用值
正确重写equals
方法
-
使用
==
检查“自反性”if (this == obj) return true;
-
使用
instanceof
检查类型if (!(obj instanceof MyClass)) return false;
-
比较关键字段
MyClass other = (MyClass) obj; // 类型转换 return field1.equals(other.field1) && field2 == other.field2;
-
考虑性能和空值
对可能为
null
的字段,使用Objects.equals
或提前检查:Objects.equals(field1, other.field1)
完整代码
import java.util.Objects;
public class Person {
private final String name;
private final int id;
public Person(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object obj) {
// 自反性检查
if (this == obj) return true;
// 类型检查
if (!(obj instanceof Person)) return false;
// 强制转换后比较字段
Person other = (Person) obj;
return id == other.id && Objects.equals(name, other.name);
}
@Override
public int hashCode() {
// 始终与 equals 一致
return Objects.hash(name, id);
}
}
需要注意:
- 如果重写了
equals
,也必须重写hashCode
,否则会违反hashCode
的契约规则,导致在集合(如HashMap
、HashSet
)中行为异常。 - 不要在
equals
方法中比较不会影响逻辑相等性的字段,如缓存值或计算中间结果。 - 如果一个对象的字段参与了
equals
的比较逻辑,建议该字段是不可变的(final
),以避免修改后违反一致性。 - 如果类实现了
Comparable
接口,那么equals
的定义应该与compareTo
保持一致,即x.compareTo(y) == 0
时,x.equals(y)
必须返回true
。
违法约定的风险
- 在集合中的行为异常。 如果
equals
和hashCode
不一致,可能导致HashMap
或HashSet
无法正确存取数据。 - 不符合用户预期。如果
equals
实现不符合通用约定,可能在业务逻辑中引入难以发现的错误。
自动工具
- 可以使用一些注解自动生成
equals
等方法。如lombok
- 使用IDEA快速生成