lombok 是什么?
lombok 是一个非常神奇的 java 类库,会利用注解自动生成 java Bean 中烦人的 Getter、Setting,还能自动生成 logger、ToString、HashCode、Builder 等 java
特色的函数或是符合设计模式的函数,能够让你 java Bean 更简洁,更美观。

来先看下使用 lombok 后的 java bean 看起来是怎样的

@Data
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;

public static void main(String[] args) {
User user = new User(1L,"张三",18);
System.out.println("toString:"+user);
System.out.println("name:"+user.getName());
}
}
输出如下

toString():User(id=1, name=张三, age=18)
getName:张三
看到了吗,仅仅在类中添加 @Data 注解就能做到自动生成 Getter 和 ToString 函数了! 仅仅添加了 @AllArgsConstructor 注解就再也不用写那些让你心烦的构造函数了!

我第一次使用 lombok 的时候就很喜欢它了。觉得它的思想非常好的,即是不应该花时间去写重复的代码,应该让之自动化。

当然自动化的手段是什么也很重要,我可以通过 ide 的生成器功能、或者是自己写的代码生成器,自动生成 Getter、Setting、ToString,就算不用 lombok 也是可以做到的。而我喜欢 lombok 最主要原因就在于它会让代码更简洁、阅读起来更清晰。

可能会有人说这玩意只适合个人项目,不适合大型合作。。。但知乎有位大神说过亚马逊(重度使用 java 的公司) 内部的项目都在用。不管如何还是先了解再决定吧,我个人还是很喜欢用这种方式。

上面的介绍了一下 lombok 有点简陋,所以下面会介绍得更详细的一点。当然也许会有读者对 lombok 这种像是魔法的写法感到好奇,究竟它是怎么自动生成代码的呢?所以文章的后面会对 lombok 进行简单的探讨,希望读者会喜欢。

lombok 的安装
安装
得现代的依赖管理 ,引入 lombok 依赖及其简单

meavn
使用 meavn 的朋友在 pom.xml 文件中添加依赖即可

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
gradle
用 gradle 的朋友 在 build.gradle 在添加依赖即可

repositories {
mavenCentral()
}

dependencies {
compileOnly 'org.projectlombok:lombok:1.18.6'
annotationProcessor 'org.projectlombok:lombok:1.18.6'
}
idea 的插件
你看 pom.xml 的依赖的作用域(scope) 是 provided,也就是说 lombok 是在编译的时候才起作用的。因此 idea 在正确使用 lombok 的时候也会报错的
所以你要安装 lombok 插件才能正常使用。

使用
注解的类型
类型 解释
val,var 神奇的类型推到,可以代表任意类型
@Getter and @Setter
@ToString
@EqualsAndHashCode
@NonNull
@AllArgsConstructor、@RequiredArgsConstructor、@NoArgsConstructor 构造函数部分,针对不同情况的构造函数
@Data 相当于 @Getter + @Setter + @ToString + @EqualsAndHashCode + RequiredArgsConstructor
@Value 类变成只读模式
@Builder builder 模式,会创建内 Builder
@Singular 要配合 builder 使用,会对(List、Set)等生成更方便函数
@Cleanup 告别烦人的释放的资源
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j
@CommonsLog, @JBossLog, @Flogger 不同框架的日志注解
@SneakyThrows 偷偷摸摸地抛出异常
@Delegate 带实验性质的,能非常方便实现代理模式
@Accessors 带实验性质的存取器
@Wither 带实验性质的,根据被修饰的成员变量创建类
val,var
可以表示任何类型!
var 可以用来表示变量,类似其他语言中的 let
val 可以用来表示常量(final),类似其他语言中的 const

var str = "hello world";
val list = Arrays.asList(1,2,3,4);
System.out.println(str);
for(val item : list){
System.out.printf("%dt",item);
}
等价于
String str = "hello world";
final List<Integer> list = Arrays.asList(1,2,3,4);
System.out.println(str);
for(final Integer item : list){
System.out.printf("%dt",item);
}
@Getter、@Setter
添加了注解后会根据字段生成对应的 get、set 函数,可以修饰成员变量或者类

@Getter
@Setter
public class User {
private Long id;
private String name;
private Integer age;
}
灵活的 lombok 可以通过,下面的方式指定访问级别(PUBLIC、PROTECTED、PACKAGE、PRIVATE)

@Getter
@Setter
public class User {
private Long id;
private String name;

@Setter(AccessLevel.PROTECTED)
private Integer age;
}
@ToString

@ToString
public class User {
private Long id;
private String name;
private Integer age;
}
ToString 生成后代码大概如下

public String toString() {
return "User(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
选项:

1、@ToString(includeFieldNames=false) 不显示变量名,会直接输出值

public String toString() {
return "User(" + this.id + ", " + this.name + ", " + this.age + ")";
}
2、@ToString(exclude = {"age"}) 生成的结果会排出 age 变量

public String toString(){
return "User(id=" + this.id + ", name=" + this.name + ")";
}
3、@ToString(of = {"id","name"}) 生成的结果包括

public String toString(){
return "User(id=" + this.id + ", name=" + this.name + ")";
}
@EqualsAndHashCode
只能用于修饰类。

@EqualsAndHashCode
public class User {
//...
}
和 ToString 类似,可以用 of 以及 exclude 来排出成员变量

@NonNull
可以用于成员变量、本地变量、参数、方法

@Setter
public class User {
private Long id;

@NonNull
private String name;
private Integer age;

}
setName 函数实际上会变成这样

public void setName(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked @NonNull but is null");
} else {
this.name = name;
}
}
构造函数 @AllArgsConstructor、@RequiredArgsConstructor、@NoArgsConstructor
这三者都是处理构造函数的注解,都只能修饰类,都能通过staticName 创建静态工厂方法,使用access控制访问级别。
不同之处在于 @AllArgsConstructor 会把所有的成员变量都纳入到构造函数中, @RequiredArgsConstructor 只会把 final 和 @NonNull 修饰的成员变量纳入、@NoArgsConstructor 所有的成员变量都不会纳入到构造函数。

@AllArgsConstructor
构造函数会包含所有字段

@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}
会自动生成

public User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
关于 staticName 和 access 的选项,可以看下面的例子

@AllArgsConstructor(staticName = "of",access = AccessLevel.PRIVATE)
public class User {
private Long id;
private String name;
private Integer age;
}
会看到构造函数和静态工厂函数的访问级别都变成 private 了

public class User {
private Long id;
private String name;
private Integer age;

private User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}

private static User of(Long id, String name, Integer age) {
return new User(id, name, age);
}
}
@RequiredArgsConstructor
用 final 修饰和 @NonNull 修饰的参数才会加入构造函数

@RequiredArgsConstructor
public class User {
@NonNull
private Long id;
private final String name;
private Integer age;
}
生成的结果大概是这样

public class User {
@NonNull
private Long id;
private final String name;
private Integer age;

public User(@NonNull Long id, String name) {
if (id == null) {
throw new NullPointerException("id is marked @NonNull but is null");
} else {
this.id = id;
this.name = name;
}
}
}
@NoArgsConstructor
顾名思义,使用 @NoArgsConstructor 会生成没有参数的构造函数

但如果是用 final 修饰的成员函数呢?

答:这样会编译出错的,除非是用 @NoArgsConstructor(force=true),那么所有的 final 字段会被定义为0,false,null等。

如果使用使用的是 @NonNull 修饰的成员字段呢?那么使用无参数的构造函数构造出来的实例成员变量不就是 null 了吗?不就矛盾了吗?

答:是的。。。

比如
@NoArgsConstructor
@Getter
public class User {
private Long id ;
private @NonNull String name;
private Integer age;

public static void main(String[] args) {
System.out.println(new User().getName());
}
}
输出结果是 null

因此如果有 @NonNull 修饰的成员的变量就不要用 @NoArgsConstructor 修饰类

@Data
@Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor 就是那么的方便

选项,可以通过 staticConstructor 创建静态工厂函数

@Data(staticConstructor = "of")
public class User {
private Long id ;
private @NonNull String name;
private Integer age;
}
@Value
将类变成只读模式。会让所有类成员变量都变成 final,然后 + @RequiredArgsConstructor + @ToString + @EqualsAndHashCode

@Builder
面对复杂的数据结构,使用 builder 模式可以抽离复杂的构造方式,能保证线程安全,我在这篇文章中也有对 Builder 的进行粗略的探讨。

使用 Builder 模式很爽,比如是这样的

User user = User.builder().id(1L)
.name("张三")
.age(12).build();
这样虽然好爽,但

代价是什么呢?

就是好多冗余的东西要写。比如是这样
public class User {
private Long id;
private String name;
private Integer age;

User(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}

public static User.UserBuilder builder() {
return new User.UserBuilder();
}

public static class UserBuilder {
private Long id;
private String name;
private Integer age;

UserBuilder() {
}

public User.UserBuilder id(Long id) {
this.id = id;
return this;
}

public User.UserBuilder name(String name) {
this.name = name;
return this;
}

public User.UserBuilder age(Integer age) {
this.age = age;
return this;
}

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

public String toString() {
return "User.UserBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
}
}
用 lombok 后

@Builder
public class User {
private Long id;
private String name;
private Integer age;
}
清晰明了,爽!

但是这里有个很严重的问题,就是不符合 java bean 的规范,java bean 要求有一个无参数的构造函数的。不符号 java bean 要求会有什么后果能?比如:json 字符不能反序列化成 java 对象。
解决方式是写成这样,要同时写上 @AllArgsConstructor 和 @NoArgsConstructor 才行

@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}