可通过反射实现通用equals方法,核心是遍历非static非transient字段并递归比较值,需处理null、类型校验、循环引用等边界情况,适合遗留系统兜底或基类统一继承。

可以通过反射遍历对象所有字段,逐个比较值是否相等,来实现一个通用的 equals 方法。核心是避开手动重写每个类的 equals,同时处理 null、类型、数组、嵌套对象等边界情况。
关键思路:用反射获取并比对非静态字段
Java 的 equals 合约要求自反性、对称性、传递性、一致性与对 null 的处理。通用实现需:
- 先做引用相等(
==)和 null 判断 - 检查 class 类型是否相同(避免跨类误判)
- 获取当前类及其父类中所有 非 static、非 transient 字段(通常业务属性都满足)
- 对每个字段,用
Field.get(obj)取值,并递归或委托比较(如用Objects.equals)
基础实现(支持基本类型、包装类、String、集合、数组)
以下是一个轻量、可复用的工具方法:
public static boolean deepEquals(Object a, Object b) {
if (a == b) return true;
if (a == null || b == null) return false;
if (!a.getClass().equals(b.getClass())) return false;
Field[] fields = a.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getAnnotation(Transient.class) != null || Modifier.isStatic(f.getModifiers())) {
continue;
}
f.setAccessible(true);
try {
Object va = f.get(a);
Object vb = f.get(b);
if (!Objects.equals(va, vb)) {
return false;
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return true;
}
使用时,在具体类中只需:
@Override
public boolean equals(Object o) {
return ReflectUtils.deepEquals(this, o);
}
注意事项与增强建议
生产环境使用需注意:
-
性能开销:反射比直接字段访问慢,高频调用场景(如大量集合去重)建议缓存
Field[]或用字节码生成(如 Lombok、AutoValue) -
忽略字段控制:可通过自定义注解(如
@EqualsExclude)替代硬编码判断,提升可读性 -
循环引用风险:若 A 引用 B,B 又引用 A,递归
deepEquals会栈溢出;可引入IdentityHashMap记录已比较过的对象对 - 敏感字段屏蔽:如密码、token 等不应参与 equals 判断,必须显式排除(注解或白名单机制)
更稳妥的落地方式:结合 Lombok 或 Record
对于新项目或可改造类,推荐:
- 用
@Data(Lombok)自动注入equals+hashCode,默认排除 static/transient,支持@EqualsAndHashCode.Exclude - Java 14+ 使用
record,编译器自动生成结构化 equals(仅比较 components,天然不可变、无循环引用问题)
反射方案适合作为遗留系统兜底手段,或封装成基类/抽象模板供内部统一继承,避免散落各处的手动实现。










