应使用EqualsVerifier自动化验证equals和hashCode契约,覆盖自反性、对称性、传递性、一致性、null比较、hashCode一致性及相等必同哈希七项要求,并辅以手动测试业务逻辑和边界场景。

要验证自定义类的 equals 和 hashCode 是否严格符合 Java 规范契约,不能只靠手写几个简单用例,而应系统性地覆盖所有契约要求,并借助工具辅助检测边界和一致性问题。
理解 equals 和 hashCode 的核心契约
Java 要求二者必须满足以下关键约束:
-
equals 自反性:对任意非 null 引用
x,x.equals(x)必须返回true -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true -
传递性:若
x.equals(y) && y.equals(z)为true,则x.equals(z)也必须为true -
一致性:多次调用
equals(对象未被修改)结果必须相同 -
与 null 比较:对任意非 null 引用
x,x.equals(null)必须返回false -
hashCode 一致性:同一对象多次调用
hashCode()必须返回相同整数(在 JVM 实例生命周期内) -
相等必同哈希:若
x.equals(y)为true,则x.hashCode() == y.hashCode()必须成立
使用 EqualsVerifier 进行自动化契约验证
EqualsVerifier 是最成熟、专为此场景设计的开源库(Maven 坐标:nl.jqno.equalsverifier:equalsverifier),它能自动检查全部七条契约,包括易被忽略的传递性与子类继承问题。
基本用法示例:
立即学习“Java免费学习笔记(深入)”;
@Test
public void testEqualsAndHashCodeContract() {
EqualsVerifier.simple().forClass(Person.class).verify();
}
若类含不可变字段、继承关系、或使用了 Lombok(如 @Data),可进一步配置:
- 处理继承:
withRedefinedSuperclass() - 排除 transient 字段:
usingGetters()或withPrefabValues() - Lombok 支持:
suppress(Warning.STRICT_INHERITANCE)等
手动补充边界与业务逻辑测试
EqualsVerifier 不验证业务语义是否合理(例如:是否该忽略空格?时间精度是否统一?),需额外编写针对性用例:
- 创建两个内容相同但字段顺序/空格/大小写不同的对象,确认
equals返回true - 构造一个字段值近似但不相等的对象(如
"123"vs123),确认equals返回false - 对同一个对象反复调用
hashCode(),断言结果恒定 - 对
equals为true的多个对象,批量校验其hashCode()全部相等(建议用Set收集后判断 size == 1) - 显式测试
obj.equals(null)和obj.equals(new Object())是否都返回false
避免常见陷阱的实践提醒
即使测试通过,也要警惕这些典型错误:
- 在
equals中使用instanceof判断类型 → 若类可被继承,会导致不对称(子类实例instanceof父类为 true,反之不成立);改用getClass() == other.getClass() -
hashCode中遗漏某个参与equals比较的字段 → 违反“相等必同哈希” - 在
equals或hashCode中调用可能抛异常或依赖外部状态的方法(如数据库查询、网络请求)→ 违反一致性与无副作用原则 - 使用浮点数直接比较(
float/double)→ 应用Float.compare或Double.compare,而非==


















