
lombok 的 @data 注解会为标注类自动生成基于字段的 equals() 方法,但不会递归包含父类字段(尤其当父类为抽象类且未加 @data 时),导致子类实例在字段全为空时意外相等。
lombok 的 @data 注解会为标注类自动生成基于字段的 equals() 方法,但不会递归包含父类字段(尤其当父类为抽象类且未加 @data 时),导致子类实例在字段全为空时意外相等。
在您的代码中,KA 类使用了 @Data(隐含于 @Getter/@Setter + @NoArgsConstructor/@AllArgsConstructor 组合中,但更关键的是——您实际并未对 KA 显式添加 @Data;然而问题根源恰恰在于:@Data 被错误地应用于抽象父类 A,而 KA 仅用了 @Getter/@Setter 等基础注解。这造成一个关键矛盾:@Data 在抽象类 A 上生成了 equals() 和 hashCode(),但 Lombok 不会为抽象类生成可调用的实例方法实现;更重要的是,当子类 KA 未显式声明 @EqualsAndHashCode 时,Lombok 默认为其生成的 equals() 仅基于 KA 自身声明的字段(即 pck, field1, field2),而完全忽略继承自 A 的 myEnum 字段。
更严重的是:由于 KA ka1 = new KA() 和 KA ka2 = new KA() 均通过无参构造创建,所有字段(包括 pck, field1, field2)均为 null,因此 KA 自动生成的 equals() 判定二者“字段完全一致”,返回 true —— 这显然违背了您期望的“引用相等”语义。
✅ 正确解决方案如下:
方案一(推荐):禁用 Lombok 自动生成 equals,显式委托给 Object.equals
在 KA 类上添加 @EqualsAndHashCode(doNotUseGetters = true, callSuper = false) 并设置 exclude 或直接禁用,但最简洁方式是显式覆盖 equals 和 hashCode:
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class KA extends A {
private MyPackage pck;
private String field1;
private String field2;
// 强制使用 Object 的引用比较
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
}方案二:若需字段语义相等,确保继承字段被纳入
为 KA 显式启用 @EqualsAndHashCode(callSuper = true),并确保父类 A 的字段参与比较:
// 修改父类 A:移除 @Data(因其不适用于抽象类),改用 @EqualsAndHashCode
@EqualsAndHashCode(callSuper = true) // 显式启用,且包含父类字段
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public abstract class A {
private Enum myEnum;
}
// 子类 KA 显式继承父类 equals 逻辑
@EqualsAndHashCode(callSuper = true)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class KA extends A {
private MyPackage pck;
private String field1;
private String field2;
}⚠️ 注意事项:
- @Data 不应作用于抽象类——Lombok 文档明确指出,@Data 生成的 toString()、equals()、hashCode() 等方法在抽象类中无意义且易引发行为歧义;
- @SuperBuilder 本身不干预 equals,但其与 @Data 混用可能加剧理解偏差;
- 若业务逻辑确实要求“同一类型实例默认按引用比较”,覆盖 equals 是最清晰、最可控的方式,避免依赖 Lombok 的隐式行为;
- 使用 System.identityHashCode() 配合 this == o 可严格保证与 Object.equals 语义一致,且线程安全。
总结:Lombok 的便利性以约定为前提,但抽象继承结构需更精确的注解控制。放弃对抽象父类使用 @Data,并在子类中显式声明 equals 策略,是保障对象语义正确性的最佳实践。






