Lombok的@Data注解会为标注类自动生成equals()方法,但仅基于该类自身声明的字段,不包含继承自父类的字段;当父类含字段而子类使用@Data时,equals逻辑失效,导致不同实例意外相等。
lombok的@data注解会为标注类自动生成equals()方法,但仅基于该类**自身声明的字段**,不包含继承自父类的字段;当父类含字段而子类使用@data时,equals逻辑失效,导致不同实例意外相等。
在您的代码中,KA 类使用了 @Data(虽问题中写为 @Getter @Setter,但实际行为与 @Data 一致),而其父类 A 中定义了 private Enum myEnum;。关键问题在于:Lombok 不会将父类字段纳入子类自动生成的 equals() 方法中。
这意味着 KA 的 equals() 仅比较 pck、field1、field2 这三个字段——而它们在两个空构造实例中均为 null,因此 ka1.equals(ka2) 返回 true,看似“总是相等”,实则是 equals() 逻辑缺失所致,并非 JVM 层面的引用比较。
✅ 正确做法:禁用 Lombok 自动生成 equals,回归 Object 默认语义
若您明确希望 KA 使用 Object.equals()(即基于内存地址的引用比较),切勿使用 @Data 或 @EqualsAndHashCode,并确保不继承任何重写 equals() 的父类。修改建议如下:
// 移除 @Data;仅保留必要注解
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class KA extends A {
private MyPackage pck;
private String field1;
private String field2;
}此时 KA 不含任何 equals() 实现,将自然继承 Object.equals(),以下断言成立:
立即学习“Java免费学习笔记(深入)”;
KA ka1 = new KA(); KA ka2 = new KA(); System.out.println(ka1.equals(ka2)); // false —— 引用不同,结果为 false System.out.println(ka1 == ka2); // false
⚠️ 注意事项与常见误区
- @Data ≠ 全继承字段参与比较:Lombok 明确规定 @Data 生成的 equals()/hashCode() 仅作用于当前类显式声明的非静态、非瞬态字段,父类字段被完全忽略(官方文档说明)。
-
@EqualsAndHashCode 可显式控制:如需基于字段的值比较(含父类字段),应显式在子类中使用:
@EqualsAndHashCode(callSuper = true) // 关键:启用父类字段参与计算 public class KA extends A { /* ... */ }此时 myEnum 将被纳入 equals() 和 hashCode() 逻辑。
- 抽象父类也需谨慎:A 被标记为 abstract 且含字段,本身不应被实例化,但其字段语义仍需在子类比较逻辑中体现——这进一步凸显 callSuper = true 的必要性(若选择值比较)。
- 避免混合注解冲突:同时使用 @Data(隐含 @EqualsAndHashCode)和手动添加 @EqualsAndHashCode(callSuper=true) 会导致编译错误,务必统一策略。
✅ 总结
| 目标 | 推荐方案 |
|---|---|
| 严格引用比较(默认 Object 行为) | ✅ 移除 @Data,不添加任何 @EqualsAndHashCode;确保无其他 equals() 实现 |
| 完整值比较(含父类字段) | ✅ 使用 @EqualsAndHashCode(callSuper = true) 替代 @Data |
| 完全自定义比较逻辑 | ✅ 手动重写 equals() 和 hashCode(),或使用 @EqualsAndHashCode(of = {...}) 精确指定字段 |
牢记:Lombok 是代码生成工具,不是语义推理引擎。字段继承关系需开发者显式声明参与行为,不可依赖自动推导。






