lombok @Builder注解 和 @Accessors

建造者模式

什么是建造者模式

建造者模式又叫创建者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象

主要作用

在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象

@Builder注解

为什么要使用Builder

当一个类的成员变量比较多,同时,大部分的成员变量有默认值仅有少量的成员变量需要初始化时,我们该如何设计这个类?我们需要让该类满足以下两个要求:

方便用户创建复杂的对象(不需要知道实现过程),仅需要初始化必要的成员变量;
代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)

例如:有一个类的成员变量很多,复杂。那么就会有很多种类的构造函数。这个时候就会需要一种方式能够动态的创建这个类

而这个时候这种设计场景就与建造者模式不谋而合。为此lombok就推出一款注解 @Builder

@Builder流程分析

编译前代码

@Data
@Builder
public class Student {
    String name;
    int age;
}

反编译后主要代码

public class Student {
    String name;
    int age;

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

    public static Student.StudentBuilder builder() {
        return new Student.StudentBuilder();
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

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

    public void setAge(final int age) {
        this.age = age;
    }
    public String toString() {
        return "Student(name=" + this.getName() + ", age=" + this.getAge() + ")";
    }
    public static class StudentBuilder {
        private String name;
        private int age;

        StudentBuilder() {
        }

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

        public Student.StudentBuilder age(final int age) {
            this.age = age;
            return this;
        }

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

        public String toString() {
            return "Student.StudentBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}

测试代码

@Test
public void testBuilder(){
    Student student = Student.builder()
    		.name("18")
    		.age(19)
    		.build();
    System.out.println(student.toString());
}

由此可以看出@Builder下的使用方式主要是通过以下几个方法,实现分步构造

 public static Student.StudentBuilder builder() {
        return new Student.StudentBuilder();
 }

静态内部类 StudentBuilder

public static class StudentBuilder {
    private String name;
    private int age;

    StudentBuilder() {
    }

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

    public Student.StudentBuilder age(final int age) {
        this.age = age;
        return this;
    }

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

通过类方法builder(), 创建一个StudentBuilder()的类。然后这个类通过返回自身(this) 的链式编程可以动态的构造对象,最后通过build 构造出一个新的Student的对象,实现了构造过程展现最后对象的解耦合

而在这个过程中builder()就是抽象建造者,它决定了由哪个实际的builder类(StudentBuilder)去创建它。而在完成对创建顺序的控制完成创建过程的分离这个过程的也就是指挥者的功能。如
Student.builder().name("18").age(19).build();

优缺点

优点

不需些太多的set方法来定义属性内容
写法更优雅

缺点

在整个过程中会多创建个中间类StudentBuilder实现解耦合,多占用了内存。
由于bulider()方法是类方法,所以在进行创建过第一次后,不能对创建后的对象继续进行动态赋值。

额外

@Builder(toBuilder = true)

这个选项允许你将一个实例化好的Card更新字段生成新的Card实例。

public Card.CardBuilder toBuilder() {
    return (new Card.CardBuilder()).id(this.id).name(this.name).sex(this.sex);
}

可以清楚的看出来,toBuilder方法是用当前实例的属性构造了一个新的Builder实例。

@Accessors

该注解主要作用是:

当属性字段在生成 getter 和 setter 方法时,做一些相关的设置。
当它可作用于类上时,修饰类中所有字段,当作用于具体字段时,只对该字段有效。

源码

package lombok.experimental;

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Accessors {
    boolean fluent() default false;

    boolean chain() default false;

    String[] prefix() default {};
}

属性

fluent 属性

不写默认为false
当该值为 true 时,对应字段的 getter 方法前面就没有 get,setter 方法就不会有 set。

在这里插入图片描述
在这里插入图片描述

chain 属性

不写默认为false
当该值为 true 时,对应字段的 setter 方法调用后,会返回当前对象。

在这里插入图片描述

prefix 属性

该属性是一个字符串数组
当该数组有值时,表示忽略字段中对应的前缀,生成对应的 getter 和 setter 方法

比如现在有 xxName 字段 yyAge 字段xx yy 分别是 name 字段和 age 字段的前缀

那么,我们在生成的 getter 和 setter 方法如下,它也是带有 xx 和 yy 前缀的
在这里插入图片描述
前缀加到 @Accessors 的 prefix 属性值中,则可以像没有前缀那样,去调用字段的 getter和 setter 方法
在这里插入图片描述
在这里插入图片描述

@Accessors流程分析

编译前代码

@Data
@Accessors(chain = true)
public class Student {
    String name;
    int age;
}

反编译后的主要代码

public class Student {
    String name;
    int age;

    public Student() {
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

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

    public Student setAge(final int age) {
        this.age = age;
        return this;
    }
}

测试代码

@Test
public void testBuilder(){
    Student student = new Student().setAge(18).setName("chen");
    System.out.println(student.toString());

}

可以看到通过设置chain = true 开启了链式构造,在自身赋值的时候,返回自身对象避免了多余bean的创建,以及可以继续通过自身进行链式赋值

对比

1 两者都可以实现链式构造结合

2 如果想要实战方便些可以使用@Accessors, 如果想要更贴合建造者模式进行解耦合的话可以使用@Builder

3 @Builder就是基于建造者模式支持链式操作,但很多时候都是构造失血模式的Bean或者没有共享变量,这时候为了链式操作就新建一个builder是不是有点大材小用
@Accessors就可以解决上述的问题,支持链式操作同时减少多余对象的创建builder类元信息又可以减少

注意

使用@Builder 第二次链式赋值

如果一定要使用@Builder进行对象的 第二次以后的继续链式赋值 可以结合 toBuild=true 使用
即@Builder(toBuilder = true)

@Test
publicvoid testBuilder(){
    Student student = Student.builder().sAge(19).sName("chen").build();
    student.toBuilder().sAge(20);
}

@Data和@Builder 一起使用,要加@NoArgsConstructor和@AllArgsConstructor

@Builder@Data共用时, 注意加 @NoArgsConstructor @AllArgsConstructor 注解

因为@Builder生成的全参构造器, 与@Data所包含的@RequiredArgsConstructor发生冲突

@Data@Builder一块儿就没有了默认的构造方法
在这里插入图片描述

BeanUtils.copyProperties失效

@Accessors(chain = true)注解 时, 由于set方法带返回值, 导致一些BeanUtils.copyProperties失效

解决方法: 导包时选择这个

import org.springframework.beans.BeanUtils;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值