Lombok之@Builder注解

前言

Lombok大家都知道,在使用POJO过程中,它给我们带来了很多便利,省下大量写getset方法、构造器、equaltoString方法的时间。除此之外,通过@Builder注解,lombok还可以方便的实现建造者模式。

认识@Builder注解

lombok注解在java进行编译时进行代码的构建,对于java对象的创建工作它可以更优雅,不需要写多余的重复的代码,这对于JAVA开发人员是很重要的,在出现lombok之后,对象的创建工作更提供Builder方法,它提供在设计数据实体时,对外保持private setter,而对属性的赋值采用Builder的方式,这种方式最优雅,也更符合封装的原则,不对外公开属性的写操作!

@Builder声明实体,表示可以进行Builder方式初始化,@Value注解,表示只公开getter,对所有属性的setter都封闭,即private修饰,所以它不能和@Builder现起用

简单使用

在项目生成的实体类上,只需要我们添加@Builder注解即可。示例代码:

package com.zy.pagehelper.model;

import lombok.Builder;
import lombok.Data; import java.io.Serializable; @Data
@Builder
public class Admin implements Serializable { private Long id; private String department; private String email; private String encodedpassword; private String name; private String username; private static final long serialVersionUID = 1L;
}

项目中使用。代码实例:

 Admin admins =  Admin.builder()
.id(admin.getId())
.name(admin.getName())
.email(admin.getEmail())
.department(admin.getDepartment())
.username(admin.getUsername())
.build();

根据上面的示例,我们可以对@Builder注解有一个简单的认识。当我们向一个对象赋值的时候,可以通过@Builder注解类似于链式的调用对象进行赋值。它的主要优点就是可以优雅的给对象赋值,修改对象,省去了set方法来定义属性内容。

深入探究--原理

如果对建造者设计模式不太清楚的,可以先了解一下:建造者模式

@Builder注释为你的类生成相对略微复杂的构建器API@Builder可以让你以下面显示的那样类似于链式的调用你的代码,来初始化你的实例对象:

 Admin admins =  Admin.builder()
.id(admin.getId())
.name(admin.getName())
.email(admin.getEmail())
.department(admin.getDepartment())
.username(admin.getUsername())
.build();

@Builder可以放在类,构造器或方法上。虽然“基于类”和“基于构造器”模式是最常见的用例,但使用“方法”用例最容易解释。

@Builder注解的方法(从现在开始称为target)将生成以下7件事:

  1. 一个内部静态类,名为FooBuilder,其类型参数与静态方法相同(称为builder)
  2. 在构建器中:目标的每个参数有一个私有的非静态非最终字段
  3. 在builder中:包私有的无参数空构造函数
  4. 在builder中:对目标的每个参数使用类似于“ setter”的方法:与该参数具有相同的类型和相同的名称。如上例所示,它返回构建器本身,以便可以将setter调用链接起来
  5. 在builder中:build()调用该方法的方法,并在每个字段中传递。它返回与目标返回相同的类型
  6. 有意义的toString()实现
  7. 在包含target的类中:一个builder()方法,该方法创建builder的新实例

下面我们通过class类,与我们上面的每一条进行对比:

@Builder
public class Card {
private int id;
private String name;
private boolean sex;
}

使用@Builder注解反编译后的class类:

public class Card {
private int id;
private String name;
private boolean sex; Card(int id, String name, boolean sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
//注解在编译后使得Card类中多了一个名为Card.CardBuilder的静态内部类
public static Card.CardBuilder builder() {
return new Card.CardBuilder();
} public static class CardBuilder {
private int id;
private String name;
private boolean sex; CardBuilder() {
}
//通过静态内部类,实现了name、sex、id等的属性方法
//其实这些方法和setAttribute十分类似,只是额外返回了实例本身,这使得它可以使用类似于链式调用的写法。
public Card.CardBuilder id(int id) {
this.id = id;
return this;
} public Card.CardBuilder name(String name) {
this.name = name;
return this;
} public Card.CardBuilder sex(boolean sex) {
this.sex = sex;
return this;
}
//build方法调用Card类的全参构造方法来生成Card实例
//Card类还是实现了builder方法,这个方法生成一个空的Card.CardBuilder实例。
public Card build() {
return new Card(this.id, this.name, this.sex);
} public String toString() {
return "Card.CardBuilder(id=" + this.id + ", name=" + this.name + ", sex=" + this.sex + ")";
}
}
}

使用@Builder注解有无继承

一、 无继承父类的情况

可以将@Builder注解直接放置在类上,示例代码:

@Data
@Builder
public class Student { private String schoolName;
private String grade; public static void main(String[] args) { Student student = Student.builder().schoolName("清华附小").grade("二年级").build();
// Student(schoolName=清华附小, grade=二年级)
System.out.println(student);
}
}

二、有继承父类的情况

  1. 对于父类,使用@AllArgsConstructor注解
  2. 对于子类,手动编写全参数构造器,内部调用父类全参数构造器,在子类全参数构造器上使用@Builder注解

    通过这种方式,子类Builder对象可以使用父类的所有私有属性。但是这种解法也有两个副作用:
  • 因为使用@AllArgsConstructor注解,父类构造函数字段的顺序由声明字段的顺序决定,如果子类构造函数传参的时候顺序不一致,字段类型还一样的话,出了错不好发现
  • 如果父类字段有增减,所有子类的构造器都要修改

示例代码父类

@Data
// 对于父类,使用@AllArgsConstructor注解
@AllArgsConstructor
public class Person { private int weight;
private int height;
}

示例代码子类

@Data
@ToString(callSuper = true)
public class Student extends Person { private String schoolName;
private String grade; // 对于子类,手动编写全参数构造器,内部调用父类全参数构造器,在子类全参数构造器上使用@Builder注解
@Builder
public Student(int weight, int height, String schoolName, String grade) {
super(weight, height);
this.schoolName = schoolName;
this.grade = grade;
} public static void main(String[] args) { Student student = Student.builder().schoolName("清华附小").grade("二年级")
.weight(10).height(10).build(); // Student(super=Person(weight=10, height=10), schoolName=清华附小, grade=二年级)
System.out.println(student.toString());
}
}

@Builder注解导致默认值无效问题

@Builder注解导致默认值无效---解决方案

看完上面的内容我们知道@Builder注解它可以让我们很方便的使用builder模式构建对象。但是当我们给对象赋有默认值的时候会被@Builder注解清除掉。

从下面一段代码中,我们可以更加清楚的认识到这一点:

public class BuilderTest {
@lombok.Builder
@lombok.Data
private static class Builder {
private String name = "1232";
} @Test
public void test() {
Builder builder = Builder.builder().build();
System.out.println(builder.getName());
}
}
---打印结果---
null

那么不想让这个默认值被清除,就只能用另外一个注解来对属性进行设置:@lombok.Builder.Default

public class BuilderTest {
@lombok.Builder
@lombok.Data
private static class Builder {
@lombok.Builder.Default
private String name = "1232";
} @Test
public void test() {
Builder builder = Builder.builder().build();
System.out.println(builder.getName());
}
}
---打印结果---
1232
  • 需要注意的是@lombok.Builder.Default这个注解是后来才有的,目前已知的是1.2.X没有,1.6.X中有这个注解。

@Builder注解导致默认值无效----分析原因

由上面文章内容,我们可以知道,当我们使用@Builder注解时,编译后会生成一个静态内部类,通过静态内部类,最终才实现属性的方法,但是现实的方法,并没有默认值,这就导致当我们builder之后的实体类的属性值是null。

示例代码:

//编译前
@lombok.Builder
class Example {
private String name = "123";
}
//编译后class类
class Example {
private String name; private Example(String name) {
this.name = name;
} public static ExampleBuilder builder() {
return new ExampleBuilder();
} public static class ExampleBuilder {
private String name; private ExampleBuilder() {}
//这里我们可以看到,静态内部类实现了属性方法,但是并没有对默认值做处理,
//所有builder之后返回的属性值为null
public ExampleBuilder name(String name) {
this.name = name;
return this;
} @java.lang.Override public String toString() {
return "Example(name = " + name + ")";
} public Example build() {
return new Example(name);
}
}
}

推荐参考blog@Builder注解构造器生成的详解

@Builder相关注解

@Builder.Default 使用

比如有这样一个实体类:

@Builder
@ToString
public class User {
@Builder.Default
private final String id = UUID.randomUUID().toString();
private String username;
private String password;
@Builder.Default
private long insertTime = System.currentTimeMillis();
}

在类中我在idinsertTime上都添加注解@Builder.Default,当我在使用这个实体对象时,我就不需要在为这两个字段进行初始化值,如下面这样:

public class BuilderTest {
public static void main(String[] args) {
User user = User.builder()
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user);
}
} // 输出内容:
User(id=416219e1-bc64-43fd-b2c3-9f8dc109c2e8, username=jdkong, password=jdkong, insertTime=1546869309868)

lombok在实例化对象时就为我们初始化了这两个字段值。

当然,你如果再对这两个字段进行设值的话,那么默认定义的值将会被覆盖掉,如下面这样:

public class BuilderTest {
public static void main(String[] args) {
User user = User.builder()
.id("jdkong")
.password("jdkong")
.username("jdkong")
.build();
System.out.println(user);
}
}
// 输出内容
User(id=jdkong, username=jdkong, password=jdkong, insertTime=1546869642151)

@Builder 详细配置

@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
// 如果@Builder注解在类上,可以使用 @Builder.Default指定初始化表达式
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}
// 指定实体类中创建 Builder 的方法的名称,默认为: builder (个人觉得没必要修改)
String builderMethodName() default "builder";
// 指定 Builder 中用来构件实体类的方法的名称,默认为:build (个人觉得没必要修改)
String buildMethodName() default "build";
// 指定创建的建造者类的名称,默认为:实体类名+Builder
String builderClassName() default "";
// 使用toBuilder可以实现以一个实例为基础继续创建一个对象。(也就是重用原来对象的值)
boolean toBuilder() default false; @Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
// 告诉lombok使用表达式获取值
String field() default "";
// 告诉lombok使用表达式获取值
String method() default ""; boolean isStatic() default false;
}
}

@Builder 全局配置

# 是否禁止使用@Builder
lombok.builder.flagUsage = [warning | error] (default: not set)
#是否使用Guaua
lombok.singular.useGuava = [true | false] (default: false)
# 是否自动使用singular,默认是使用
lombok.singular.auto = [true | false] (default: true)

Lombok之@Builder注解的更多相关文章

  1. java~lombok里的Builder注解

    lombok注解在java进行编译时进行代码的构建,对于java对象的创建工作它可以更优雅,不需要写多余的重复的代码,这对于JAVA开发人员是很重要的,在出现lombok之后,对象的创建工作更提供Bu ...

  2. lombok @Builder注解使用的例子、反编译之后的代码详解

    lombok的@Builder实际是建造者模式的一个变种,所以在创建复杂对象时常使用 这里对lombok的@Builder和@Data组合的使用示例 import lombok.Builder; im ...

  3. lombok 下的@Builder注解用法

    pom依赖 <dependency> <groupId>org.projectlombok</groupId>            <artifactId& ...

  4. Lombok中的@Builder注解

    1.前言 今天在看项目代码的时候, 遇到了实体类上加@Builder注解, 之前在开发的时候, 一直没有用过这个注解, 便兴致勃勃地去查了一下资料, 它也是Lombok中的注解, 我们都知道Lombo ...

  5. Java中lombok @Builder注解使用详解(十八)

    Lombok大家都知道,在使用POJO过程中,它给我们带来了很多便利,省下大量写get.set方法.构造器.equal.toString方法的时间.除此之外,通过@Builder注解,lombok还可 ...

  6. 实体继承与@Builder注解共存

    在面向对象的设计里,继承是非常必要的,我们会把共有的属性和方法抽象到父类中,由它统一去实现,而在进行lombok时代之后,更多的打法是使用@Builder来进行对象赋值,我们直接在类上加@Builde ...

  7. lombok的@builder 不能新建DO对象 Lombok存在的一些问题

    1. 实体类加上 lombok的@builder之后  就不能新建对象了,,,构造函数被覆盖了? 加上两个标签之后解决 2.Lombok存在的一些问题 lombok问题 @Builder和@NoArg ...

  8. lombok的@Accessors注解

    @AllArgsConstructor @Data @NoArgsConstructor @Accessors(chain = true) @EqualsAndHashCode public clas ...

  9. 使用lombok的@Builder的注解的一个坑

    一开发说项目报错 java.lang.Long,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lan ...

随机推荐

  1. D. Palindromic characteristics 解析(DP)

    Codeforce 835 D. Palindromic characteristics 解析(DP) 今天我們來看看CF835D 題目連結 題目 略,請看原題 前言 想不到這種狀態... @copy ...

  2. mysql在DOS环境下操作的命令

    管理员运行cmd,执行启动mysql命令:net start MySQL版本号 登录数据库:mysql -u root -p 输入密码 创建数据库:drop database if exists 数据 ...

  3. 【总结】spring aop

    1.aop简介 AOP的全称是Aspect Oriented Programming,面向切面编程.它的主要思想是在程序正常执行的某一个点切进去加入特定的逻辑.AOP框架中对AOP支持最完整的是Asp ...

  4. 快来,我悄悄的给你说几个HashCode的破事。

    这是why技术的第 72 篇原创文章 Hash冲突是怎么回事 在这个文章正式开始之前,先几句话把这个问题说清楚了:我们常说的 Hash 冲突到底是怎么回事? 直接上个图片: 你说你看到这个图片的时候想 ...

  5. pandas对列求和

    了解更多,请关注公众号"轻松学编程" 一行代码实现对列求和 使用pandas把列表中的字典元素转成二维数组,然后使用pandas函数实现对每一列求和. 代码: import pan ...

  6. 【轻松学编程】如何快速学会一门高级编程语言,以python为例

    python文章目录 关注公众号"轻松学编程"了解更多. 写在前面:如何快速(比如在一个月内)学会一门高级编程语言? 现在想学一门编程语言并不难,网上有很多资料,包括书籍.博客.视 ...

  7. Charles使用part1——基本功能介绍

    一. 安装与破解: 官网地址:https://www.charlesproxy.com/download/ 破解自行解决. 二. 启动与配置: 启动 Charles 后,第一次 Charles 会请求 ...

  8. Docker(9)- docker pull 命令详解

    如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 从镜像仓库中拉取或更新镜像 ...

  9. XJOI 夏令营501-511测试11 统计方案

    小B写了一个程序,随机生成了n个正整数,分别是a[1]..a[n],他取出了其中一些数,并把它们乘起来之后模p,得到了余数c.但是没过多久,小B就忘记他选了哪些数,他想把所有可能的取数方案都找出来.你 ...

  10. Redis基础—了解Redis是如何做数据持久化的

    之前的文章介绍了Redis的简单数据结构的相关使用和底层原理,这篇文章我们就来聊一下Redis应该如何保证高可用. 数据持久化 我们知道虽然单机的Redis虽然性能十分的出色, 单机能够扛住10w的Q ...