构建器builder模式 + lombok @Builder的介绍及使用

参考
https://juejin.im/post/6844903862600466439

注意⚠️:
带有@Builder的类,如果加了@NoArgsConstructor,那么也必须加@AllArgsConstructor,因为@Builder需要全参数的构造函数。

1、重叠构造函数的优化

对Effective Java书中第二章第二条遇到多个构造器参数时要考虑使用构建器进行复盘

假设某个类,现在有3个必选属性,有5个可选属性.(为了代码简洁,后面都只写一个必选属性,2个可选属性.懂就行).

那么现在想提供完善的创建该类的机制,该怎么办呢?

1、首先是方法1-使用重叠的构造方法.

这是大家都熟悉的方法,重载很多个构造方法,每个的参数都不一样,总有一款适合您!

public class Student {

    // 必选
    String name;
    // 可选
    int age;
    String title;

    public Student(String name) {
        this.name = name;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(String name, int age, String title) {
        this.name = name;
        this.age = age;
        this.title = title;
    }
}

三个构造方法是不是已经脑壳疼了,注意,在真正的代码中,30个属性的类也不少见噢,写死人了要.
而且这样还有一个缺点,可读性太差了,在写的时候还好一些,在调用的时候你会看到编译器提醒你有30个构造方法可以调用,并且只显示参数类型不显示参数名字(比如一个8个int参数的构造方法,鬼知道应该按照什么顺序传入啊),你根本不知道该怎么用…

2、那么还有第二种方法:javabean,即使用setter.

对每个属性都提供set方法,这样客户端可以队医的调用set方法来传入他们想要的参数.

public class Student {

    // 必选
    private String name;
    // 可选
    private int age;
    private String title;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

调用代码:

        Student student = new Student();
        student.setName("huyan");
        student.setAge(1);
        student.setTitle("666");

这样子的好处是可读性好,但是不好的地方是不安全,你根本不知道客户端会以什么奇怪的方式使用你的类.

3、可以使用Builder模式.

public class Student {

    // 必选
    private String name;
    // 可选
    private int age;
    private String title;

    private static class Builder {
        // 必选
        private String name;
        // 可选
        private int age;
        private String title;

        public Builder(String name) {
            this.name = name;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder title(String s) {
            this.title = s;
            return this;
        }

        public Student build() {
            return new Student(this);
        }

    }

    private Student(Builder builder) {
        name = builder.name;
        age = builder.age;
        title = builder.title;
    }

这里面有几个重要的点:

  • 将Student类的构造方法私有化,所以想要新建Student必须使用Builder.
  • Builder只有一个构造方法,传入必选的参数,这样可以保证每个Student都会有必选参数.
  • 对所有的可选参数提供同名方法,使得可选参数可以被设置,同时返回自身.
  • Builder提供build方法,调用Student私有的构造方法,返回对象.

客户端的调用方法如下:

    public static void main(String[] args) {
        Student s = new Builder("huyan").age(11).title("888").build();
    }

使用Builder模式实现了上面其他两种方式的优点:安全且可读性高.

  • 限制了参数,保证必选参数肯定有.
  • 可读性好,传入每个可选参数单独调用方法,可以明确的知道每个参数的意义.
  • 链式调用看起来好看.

当然,builder模式也有缺点:

  • 在创建的过程中多创建了一个对象,这对性能肯定是有影响的,所以在极限要求性能的场景可以注意一下.
  • 代码比重叠构造器的代码都多…写起来也挺累啊.

等等,老是写Builder类?lombok了解一下?只需要一个注解就可以实现上面这样子的效果噢~.所以接下来学习一下,lombok是都有哪些使用方式.

2、lombok

lombok是一套小工具,可以帮助你减少样板式或者实现一些别的功能.

lombok的作用仅在源码起作用,也就是说,lombok会帮你在编译的过程中添加一些东西,使得你不用自己写,而一旦生成了class文件,lombok的作用就已经结束.

Builder:首先看一下上面提到的Builder是怎么实现的.将Student类的代码清空,仅保留属性,然后在类名上加上@Builder注解:

@Builder
public class Student1 {

    // 必选
    private String name;
    // 可选
    private int age;
    private String title;
    
}

然后使用IDEA进行编译,然后查看class文件,Idea会自动帮我们反编译拿到源文件.查看源代码,如下:

public class Student1 {
    private String name;
    private int age;
    private String title;

    @ConstructorProperties({"name", "age", "title"})
    Student1(String name, int age, String title) {
        this.name = name;
        this.age = age;
        this.title = title;
    }

    public static Student1.Student1Builder builder() {
        return new Student1.Student1Builder();
    }

    public static class Student1Builder {
        private String name;
        private int age;
        private String title;

        Student1Builder() {
        }

        public Student1.Student1Builder name(String name) {
            this.name = name;
            return this;
        }

        public Student1.Student1Builder age(int age) {
            this.age = age;
            return this;
        }

        public Student1.Student1Builder title(String title) {
            this.title = title;
            return this;
        }

        public Student1 build() {
            return new Student1(this.name, this.age, this.title);
        }

        public String toString() {
            return "Student1.Student1Builder(name=" + this.name + ", age=" + this.age + ", title=" + this.title + ")";
        }
    }
}

可以看到,Builder的代码和上面我们自己写的一模一样,整个类的区别就是,Student1类提供了toBuilder()方法,返回一个Student1Builder,可以完全屏蔽用户对Builder类的感知.

下面列举一些常用的lombok的注解,并简要解释其作用.

  • @Getter/@Setter
    可以应用在类上,属性上. 自动生成get/set方法.
  • @toString
    自动生成toString方法.
  • @EqualsAndHashCode
    生成equals和hashcode方法.
  • @RequiredArgsConstructor
    生成一个必须参数的构造器.
  • @Data
    快捷方法,相当于@Getter+ @Setter+ toString + EqualsAndHashCode + RequiredArgsConstructor.
  • @AllArgsConstructor 和 @NoArgsConstructor
    自动生成全部参数和零个参数的构造方法.
  • @Log
    包含一系列常用的log系统的注解,比如@Slf4j,@Log4j2等,自动生成一个全局final的logger供你使用.
FreeBuilder为 Java 1.6  自动生成 Builder 模式。当设计类的构造函数或静态工厂具有多个参数时,Builder 模式是一个不错的选择。—— Effective Java,第二版,第39页背景在 Java 中实现 Builder 模式容易出错和重复。即使是一个简单的四字段类,最基本的构建器 API 需要 39 行代码,如果你不使用像 AutoValue 这样的实用程序来生成值样板,则需要 72 行代码。使用import org.inferred.freebuilder.FreeBuilder; @FreeBuilder public interface Person {   /** Returns the person's full (English) name. */   String name();   /** Returns the person's age in years, rounded down. */   int age();   /** Builder of {@link Person} instances. */   class Builder extends Person_Builder { } }JavaBean convention如果值类型遵循 JavaBean 命名约定,只需在访问器方法前面加上'get'(或者,布尔访问器是'is')。 FreeBuilder 会跟随,并在 setter 方法上添加“set”前缀,以及从 toString 输出中删除前缀。@FreeBuilder public interface Person {   /** Returns the person's full (English) name. */   String getName();   /** Returns the person's age in years, rounded down. */   int getAge();   /** Builder of {@link Person} instances. */   class Builder extends Person_Builder { } } Person person = new Person.Builder()     .setName("Phil")     .setAge(31)     .build(); System.out.println(person);  // Person{name=Phil, age=31} 标签:FreeBuilder
<think>好的,我现在需要帮助用户了解Lombok@Builder注解的使用方法。根据用户提供的引用内容,我需要整理出详细的使用步骤和示例。首先,我应该从基础用法开始,解释如何添加注解到类上,并展示生成的Builder的用法。然后,考虑是否需要覆盖默认配置,比如使用toBuilder方法。接着,用户可能对自定义构建方法感兴趣,比如修改方法名或者添加自定义逻辑。此外,结合其他注解如@Accessors的情况也需要说明,可能涉及到链式调用的问题。 用户提供的引用中有提到@Builder可以生成builder方法,并且从1.18.8版本开始支持builderMethodName参数来抑制生成方法。这一点需要明确说明,可能用户想自定义方法名或者避免冲突。另外,引用[5]提到生成的是空的CardBuilder实例,这可能是一个需要注意的地方,需要指出可能需要手动初始化某些字段。 接下来,我需要确保回答结构清晰,分点列出,每个部分都给出代码示例。比如,基础使用部分用User类作为例子,展示如何构建对象。然后,toBuilder使用示例,说明如何修改现有对象的属性。自定义构建器方法部分,可能需要展示如何用静态方法返回Builder实例,或者如何修改构建步骤。 同时,需要注意引用来源,在适当的位置添加引用标识,比如当提到生成builder类时引用[4],提到toBuilder时引用[2]。另外,用户的问题中提到了相关引用,需要确保正确对应,比如引用[3]中的User类示例,可以在基础使用部分引用。 最后,生成相关问题,需要考虑用户可能进一步想了解的内容,比如与构造函数的对比、处理继承问题、性能影响等。确保问题覆盖用户可能遇到的常见疑问。 现在需要检查是否有遗漏的细节,比如是否需要说明@Builder的默认行为,或者与其他Lombok注解的兼容性。例如,@Accessors注解可能会影响构建器的生成方式,需要指出可能的问题,如链式调用冲突。此外,需要提醒用户注意Lombok版本,某些功能如builderMethodName在特定版本后才支持。 总结步骤: 1. 基础使用:类上添加@Builder,生成构建器,示例代码。 2. 使用toBuilder复制并修改对象。 3. 自定义构建方法,包括方法名和自定义逻辑。 4. 结合其他注解的注意事项。 5. 常见问题解答,如版本要求和冲突处理。 6. 相关问题生成,覆盖进阶使用和潜在问题。</think>### Lombok @Builder 注解使用详解 #### 一、基础使用 在类上添加 `@Builder` 注解后,Lombok 会自动生成一个**构建器类**,提供链式调用方式创建对象。以用户信息类 `User` 为例[^3]: ```java import lombok.Builder; import lombok.Getter; @Builder @Getter public class User { private String name; private int age; private String email; } ``` 调用方式: ```java User user = User.builder() .name("Alice") .age(28) .email("alice@example.com") .build(); ``` 生成的构建器会包含与类属性**一一对应的方法**,每个方法返回构建器实例本身,支持链式调用[^4]。 #### 二、对象修改(toBuilder) 通过 `@Builder(toBuilder = true)` 可基于现有对象创建新实例: ```java User updatedUser = user.toBuilder() .age(29) // 修改年龄 .email("alice_new@example.com") // 更新邮箱 .build(); ``` 此时会生成包含原始对象属性的新 `User` 实例[^2]。 #### 三、自定义构建器 1. **修改构建方法名**(Lombok v1.18.8+): ```java @Builder(builderMethodName = "hiddenBuilder") public class Product { private String id; } ``` 调用时需使用自定义名称: ```java Product product = Product.hiddenBuilder().id("P1001").build(); ``` 2. **添加自定义构建逻辑**: ```java @Builder public class Order { private String orderId; private LocalDateTime createTime; public static class OrderBuilder { public OrderBuilder() { createTime(LocalDateTime.now()); // 自动设置创建时间 } } } ``` #### 四、组合注解注意事项 与 `@Accessors(chain = true)` 组合使用时,可能因**方法名冲突**导致编译错误: ```java @Builder @Accessors(chain = true) public class Account { // 链式setter与构建器方法会冲突 } ``` 此时需移除 `@Accessors` 或调整 Lombok 配置。 #### 五、常见问题解答 1. **默认值设置**: ```java @Builder.Default private int status = 1; // 字段初始化默认值 ``` 2. **版本要求**: - `toBuilder` 功能需 Lombok v1.16.18+ - `builderMethodName` 参数需 v1.18.8+[^1] 3. **继承问题**: 父类使用 `@Builder` 时,子类需手动添加 `@Builder` 并调用 `super()` 方法初始化父类字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值