jinji
发布于 2026-01-08 / 15 阅读
0
0

lombok.Builder;作用是什么,

@Builder 是 Lombok 注解,用于自动生成构造器模式的代码。主要作用:

1. 替代多个构造器

// 不使用 @Builder
public class User {
    private String name;
    private int age;
    private String email;
    
    public User(String name) { this.name = name; }
    public User(String name, int age) { this.name = name; this.age = age; }
    // 更多构造器...
}

// 使用 @Builder
@Builder
public class User {
    private String name;
    private int age;
    private String email;
}

2. 链式调用设置属性

User user = User.builder()
    .name("张三")
    .age(25)
    .email("zhangsan@example.com")
    .build();

3. 核心优势

  • 可读性强:通过方法名明确参数含义
  • 灵活性高:可以只设置需要的参数
  • 不变性支持:与 @Data 或手动 getter 配合
  • 线程安全:通常用于构建不可变对象

4. 常用配置选项

// 设置默认值
@Builder
public class User {
    @Builder.Default
    private int status = 1;
}

// 与 @AllArgsConstructor 配合
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class User {
    private String name;
    private int age;
}

// 使用 toBuilder 创建副本
@Builder(toBuilder = true)
public class User {
    private String name;
    private int age;
}

5. 编译后生成的代码

Lombok 会在编译时生成:

  • User.UserBuilder 内部类
  • builder() 静态方法
  • build() 方法
  • 所有字段的 setter 方法(返回 builder 本身)

6. 适用场景

  • DTO/VO 对象
  • 配置类
  • 需要多个可选参数的复杂对象
  • 测试数据构造

7. 注意事项

@Builder
public class User {
    // 如果有 final 字段,必须初始化
    private final String id = UUID.randomUUID().toString();
    
    // 如果有 @Builder.Default,不能在构造器中重新赋值
    @Builder.Default
    private Date createTime = new Date();
}

@Builder 简化了对象创建,特别是当对象有很多可选参数时,避免了编写多个重载构造器或冗长的 setter 调用。

@Builder.Default 是 Lombok @Builder 注解的配套注解,用于设置 Builder 的默认值

1. 基本用法

@Builder
public class User {
    private String name;
    
    @Builder.Default
    private Integer age = 18;  // 设置默认值
    
    @Builder.Default
    private Boolean active = true;
    
    // 如果没有 @Builder.Default,默认值不会被 Builder 使用
    private String role = "user";  // ❌ Builder 会忽略这个默认值
}

2. 为什么需要 @Builder.Default

@Builder
public class User {
    private String name;
    private int age = 18;  // 字段初始化
    
    public static void main(String[] args) {
        User user = User.builder()
            .name("张三")
            .build();
        
        System.out.println(user.getAge());  // 输出: 0 ❌ 不是 18!
    }
}

问题:没有 @Builder.Default 时,字段初始值会被 Builder 忽略。

3. 正确的用法

@Builder
public class User {
    private String name;
    
    @Builder.Default
    private int age = 18;  // ✅ Builder 会使用这个默认值
    
    @Builder.Default
    private List<String> hobbies = new ArrayList<>();
    
    @Builder.Default
    private Date createTime = new Date();  // 每次构建时都是新对象
}

// 使用
User user1 = User.builder()
    .name("张三")
    .build();  // age=18, hobbies=[], createTime=当前时间

User user2 = User.builder()
    .name("李四")
    .age(25)  // 可以覆盖默认值
    .build();

4. 集合和对象的特殊处理

@Builder
public class Config {
    // 集合类型 - 推荐方式
    @Builder.Default
    private List<String> permissions = new ArrayList<>(Arrays.asList("read", "write"));
    
    // 防止空指针
    @Builder.Default
    private Map<String, Object> metadata = new HashMap<>();
    
    // 复杂对象
    @Builder.Default
    private Address address = Address.builder()
        .city("北京")
        .country("中国")
        .build();
}

5. 配合其他注解使用

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Product {
    private Long id;
    
    @Builder.Default
    private String status = "DRAFT";
    
    @Builder.Default
    private LocalDateTime createdAt = LocalDateTime.now();
}

6. 继承中的使用

@SuperBuilder
public class Parent {
    @Builder.Default
    protected String parentField = "parent default";
}

@SuperBuilder
public class Child extends Parent {
    @Builder.Default
    private String childField = "child default";
}

7. 与 toBuilder 一起使用

@Builder(toBuilder = true)
public class Order {
    private Long id;
    
    @Builder.Default
    private String status = "NEW";
    
    public Order cancel() {
        return this.toBuilder()
            .status("CANCELLED")  // 可以修改 @Builder.Default 的字段
            .build();
    }
}

8. 注意事项

@Builder
public class Example {
    // ❌ 错误:final 字段不能用 @Builder.Default
    @Builder.Default
    private final String id = generateId();  // 编译错误
    
    // ✅ 正确:final 字段直接初始化
    private final String id = generateId();
    
    // ❌ 错误:静态字段不能用 @Builder.Default
    @Builder.Default
    private static String type = "DEFAULT";  // 编译错误
    
    // 线程安全问题
    @Builder.Default
    private Date timestamp = new Date();  // 每次构建都是新的 Date 对象
    
    // 共享可变对象的风险
    @Builder.Default
    private List<String> items = new ArrayList<>();
    
    public void addItem(String item) {
        // 如果多个实例共享同一个默认列表,会有问题
        // 但 @Builder.Default 每次都会创建新实例,所以安全
    }
}

9. 工作原理

编译后生成的代码:

public class User {
    private int age = 18;
    
    public static class UserBuilder {
        private boolean age$set = false;  // 标志位,记录是否设置了 age
        private int age$value;
        
        public UserBuilder age(int age) {
            this.age$value = age;
            this.age$set = true;
            return this;
        }
        
        public User build() {
            User user = new User();
            if (age$set) {
                user.age = age$value;  // 如果设置了,用设置的值
            } else {
                user.age = User.this.age;  // 否则用默认值
            }
            return user;
        }
    }
}

总结@Builder.Default 确保 Builder 模式中字段的默认值被正确使用,解决了普通字段初始化在 Builder 中被忽略的问题。


评论