一、问题
既然 @Data 已经包含构造器功能,为什么还需要 @NoArgsConstructor 和 @AllArgsConstructor
二、@Data 的构造器行为真相
1. @Data 实际包含的构造器
@Data =
@Getter +
@Setter +
@ToString +
@EqualsAndHashCode +
@RequiredArgsConstructor // 注意:不是无参或全参!
2. @RequiredArgsConstructor 的行为
public class User {
private final Long id; // final 字段
@NonNull private String name; // @NonNull 字段
private String email; // 普通字段
// @Data 生成的构造器:
public User(Long id, String name) {
this.id = id;
this.name = name;
}
}
特点:
- 只包含必须初始化的字段:
- final 字段
- @NonNull 字段
- 不包含普通可选字段
- 不生成无参构造
@NonNull 是 Lombok 中用于空值安全检查的核心注解,它通过在编译时生成空值检查代码,有效预防 NullPointerException(NPE),提升代码健壮性。与 Java 标准库中的 Objects.requireNonNull() 功能类似,但更加简洁高效。
三、为什么需要额外构造器注解
场景 1:需要无参构造器(JPA/Hibernate 要求)
@Entity
@Data
@NoArgsConstructor // 必须添加
public class Product {
@Id
@GeneratedValue
private Long id; // 非final字段
private String name;
}
原因:
- JPA 实体类必须有无参构造器
- @Data 不会自动生成无参构造
场景 2:需要全参构造器(Builder/测试)
@Data
@AllArgsConstructor // 全参构造
@Builder // 需要全参构造支持
public class Order {
private Long id;
private String orderNo;
private BigDecimal amount;
}
// 测试中使用
Order testOrder = new Order(1L, "ORD-123", new BigDecimal("99.99"));
场景 3:final 字段的类需要无参构造
@Data
@NoArgsConstructor // 需要强制添加
public class Config {
private final String env = "prod"; // final字段有默认值
private String apiKey;
}
注意:如果 final 字段没有默认值,则不能有无参构造。因为final字段要求对象创建后值不可变,若允许无参构造跳过初始化,将破坏这一语义约束。
场景 | @RequiredArgsConstructor行为 | 风险与建议 |
---|---|---|
final字段无默认值 | 生成含参构造器,Spring自动注入 | ✅ 安全,推荐依赖注入方式 |
final字段有默认值 | 忽略该字段,仅注入其他未初始化字段 | ✅ 安全,但需确保默认 |
强行使用无参构造器 | final字段赋默认值(null/0/false) | ❌ 破坏不可变性,禁止使用 |
四、构造器生成优先级
- 如果存在任何显式构造器,Lombok 不会生成默认构造器
- @RequiredArgsConstructor 优先级低于显式注解
- @NoArgsConstructor 和 @AllArgsConstructor 总是显式生成
五、核心结论
注解 | 生成构造器类型 | 是否被 @Data 包含 | 主要应用场景 |
---|---|---|---|
@Data | @RequiredArgsConstructor (部分字段构造器) | 自身主注解 | 快速创建 POJO |
@NoArgsConstructor | 无参构造器 | ❌ | 核心需求场景 |
@AllArgsConstructor | 全字段构造器 | ❌ | Builder 模式/测试 |
总之:@Data的@RequiredArgsConstructor只包含必须初始化的字段:final修饰未被初始化的字段、@NonNull 字段,不包含普通可选字段,不生成无参构造。