Lombok简介
Lombok简介
和其他语言相比,Java经常因为不必要的冗长被批评。Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁。较少的模板意味着更简洁的代码,更易于阅读和维护。在本文中,我将涉及我经常使用的Lombok功能,并想你展示如何使用他们生产更清晰、更简洁的代码。
1.局部变量类型推断:val 和 var
许多语言通过查看等号右侧的表达式来推断局部变量类型。尽管现在Java 10+已经支持这种功能,但在之前的版本中没有Lombok的帮助就无法实现。下面的代码段展示了如何显式指定局部类型:
final Map<String, Integer> map = new HashMap<>();
map.put("Joe", 21);
在Lombok中,我们可以通过使用val来缩短它,如下所示:
val valMap = new HashMap<String, Integer>();
valMap.put("Sam", 30);
注意,val在背后创建了一个final且不可变的变量。如果你需要一个可变本地变量,可以使用var。
2.@NonNull
对方法参数进行null检查通常不是一个坏主意,特别是如果该方法形成的API被其他开发者使用。虽然这些检查很简单,但是他们可能变得冗长,特别是当你有多个参数时。如下所示,额外的代码无助于可读性,并且可能从方法的主要目的分散注意力。
public void nonNullDemo(Employee employee, Account account) {
if(employee == null) {
throw new IllegalArgumentException("Employee is marked @NonNull but is null");
}
if(account == null) {
throw new IllegalArgumentException("Account is marked @NonNull but is null");
}
// do stuff
}
理想情况下,你需要null检查——没有干扰的那种。这就是@NonNull发挥作用的地方。通过用@NonNull标记参数,Lombok替你为该参数生成null检查。你的方法突然变得更加简洁,但没有丢失那些安全性的null检查。
public void nonNullDemo(@NonNull Employee employee, @NonNull Account account) {
// just do stuff
}
默认情况下,Lombok会抛出NullPointerException,如果你愿意,可以配置 Lombok抛出IllegalArgumentException。我个人更喜欢IllegalArgumentException,因为我认为它更适合于对参数检查。
##3.更简洁的数据类 数据类是Lombok真正有助于减少模板代码的领域。在查看该选项前,思考一下我们经常需要处理的模板种类。数据类通常包括以下一种或全部:
- 构造函数(有或没有参数)
- 私有成员变量的 getter 方法
- 私有非 final 成员变量的 setter 方法
- 帮助记录日志的 toString 方法
- equals 和 hashCode(处理相等/集合)
可以通过 IDE 生成以上内容,因此问题不在于编写他们花费的时间。问题是带有少量成员变量的简单类很快会变得非常冗长。让我们看看Lombok如何通过处理上述的每一项来减少混乱。
3.1. @Getter 和 @Setter
想想下面的Car类。当生成getter和setter时,我们会得到接近 50 行代码来描述一个包含 5 个成员变量的类。
public class Car {
private String make;
private String model;
private String bodyType;
private int yearOfManufacture;
private int cubicCapacity;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getBodyType() {
return bodyType;
}
public void setBodyType(String bodyType) {
this.bodyType = bodyType;
}
public int getYearOfManufacture() {
return yearOfManufacture;
}
public void setYearOfManufacture(int yearOfManufacture) {
this.yearOfManufacture = yearOfManufacture;
}
public int getCubicCapacity() {
return cubicCapacity;
}
public void setCubicCapacity(int cubicCapacity) {
this.cubicCapacity = cubicCapacity;
}
}
Lombok可以替你生成getter和setter模板。通过对每个成员变量使用 @Getter和@Setter注解,你最终得到一个等效的类,如下所示:
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
}
注意,你可以在非final成员变量上只使用@Setter。在final成员变量上使用它将导致编译错误。
如果你需要为每个成员变量生成getter和setter,你也可以在类级别使用 @Getter和@Setter,如下所示。
@Getter
@Setter
public class Car {
private String make;
private String model;
private String bodyType;
private int yearOfManufacture;
private int cubicCapacity;
}
3.2. @AllArgsConstructor
数据类通常包含一个构造函数,它为每个成员变量接受参数。IDE 为Car生成的构造函数如下所示:
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
public Car(String make, String model, String bodyType, int yearOfManufacture, int cubicCapacity) {
super();
this.make = make;
this.model = model;
this.bodyType = bodyType;
this.yearOfManufacture = yearOfManufacture;
this.cubicCapacity = cubicCapacity;
}
}
我们可以使用@AllArgsConstructor注解实现同样功能。@Getter和 @Setter、@AllArgsConstructor减少模板,保持类更干净且更简洁。
@AllArgsConstructor
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
}
还有其他选项用于生成构造函数。@RequiredArgsConstructor将创建带有每个 final成员变量参数的构造函数,@NoArgsConstructor将创建没有参数的构造函数。
3.3. @ToString
在你的数据类上覆盖toString方法是有助于记录日志的良好实践。IDE 为Car类生成的toString方法如下所示:
@AllArgsConstructor
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
@Override
public String toString() {
return "Car [make=" + make + ", model=" + model + ", bodyType=" + bodyType + ", yearOfManufacture="
+ yearOfManufacture + ", cubicCapacity=" + cubicCapacity + "]";
}
}
我们可以使用ToString注解废除这个,如下所示:
@ToString
@AllArgsConstructor
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
}
默认情况下,Lombok生成包含所有成员变量的toString方法。可以通过 exclude属性@ToString(exclude={"someField"}, "someOtherField"}) 覆盖行为将某些成员变量排除。
3.4. @EqualsAndHashCode
如果你正在将你的数据类和任何类型的对象比较,则需要覆盖equals和hashCode 方法。对象的相等是基于业务规则定义的。举个例子,在Car类中,如果两个对象有相同的make、model和bodyType,我可能认为他们是相等的。如果我使用 IDE 生成equals方法检查make、model和bodyType,它看起来会是这样:
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Car other = (Car) obj;
if (bodyType == null) {
if (other.bodyType != null)
return false;
} else if (!bodyType.equals(other.bodyType))
return false;
if (make == null) {
if (other.make != null)
return false;
} else if (!make.equals(other.make))
return false;
if (model == null) {
if (other.model != null)
return false;
} else if (!model.equals(other.model))
return false;
return true;
}
等价的hashCode实现如下所示:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((bodyType == null) ? 0 : bodyType.hashCode());
result = prime * result + ((make == null) ? 0 : make.hashCode());
result = prime * result + ((model == null) ? 0 : model.hashCode());
return result;
}
虽然 IDE 处理了繁重的工作,但我们在类中仍然有大量的模板代码。Lombok允许我们使用@EqualsAndHashCode类注解实现相同的功能,如下所示:
@ToString
@AllArgsConstructor
@EqualsAndHashCode(exclude = { "yearOfManufacture", "cubicCapacity" })
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
}
默认情况下,@EqualsAndHashCode会创建包含所有成员变量的equals和 hashCode方法。exclude选项可用于通知Lombok排除某些成员变量。在上面的代码片段中。我已经从生成的equals和hashCode方法中排除了 yearOfManuFacture 和cubicCapacity。
3.5. @Data
如果你想使数据类尽可能精简,可以使用@Data注解。@Data 是@Getter、@Setter、@ToString、@EqualsAndHashCode 和 @RequiredArgsConstructor 的快捷方式。
@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(exclude = { "yearOfManufacture", "cubicCapacity" })
public class Car {
@Getter @Setter
private String make;
@Getter @Setter
private String model;
@Getter @Setter
private String bodyType;
@Getter @Setter
private int yearOfManufacture;
@Getter @Setter
private int cubicCapacity;
}
通过使用@Data,我们可以将上面的类精简如下:
@Data
public class Car {
private String make;
private String model;
private String bodyType;
private int yearOfManufacture;
private int cubicCapacity;
}
4. 使用 @Buidler 创建对象
建造者设计模式描述了一种灵活的创建对象的方式。Lombok可以帮你轻松的实现该模式。看一个使用简单Car类的示例。假设我们希望可以创建各种Car对象,但我们希望在创建时设置的属性具有灵活性。
@AllArgsConstructor
public class Car {
private String make;
private String model;
private String bodyType;
private int yearOfManufacture;
private int cubicCapacity;
private List<LocalDate> serviceDate;
}
假设我们要创建一个Car,但只想设置make和model。在Car上使用标准的全参数构造函数意味着我们只提供make和model并设置其他参数为null。
Car2 car2 = new Car2("Ford", "Mustang", null, null, null, null);
这可行但并不理想,我们必须为我们不感兴趣的参数传递null。我们可以创建一个只接受make和model的构造函数来避开这个问题。这是一个合理的解决方法,但不够灵活。如果我们有许多不同的字段排列,我们可以用什么来创建一个新Car?最终我们得到了一堆不同的构造函数,代表了我们可以实例化Car的所有可能方式。
解决该问题的一种干净、灵活的方式是使用建造者模式。Lombok通过@Builder 注解帮你实现建造者模式。当你使用@Builder注解Car类时,Lombok会执行以下操作:
- 添加一个私有构造函数到
Car - 创建一个静态的
CarBuilder类 - 在
CarBuilder中为Car中的每个成员创建一个setter风格方法。 - 在
CarBuilder中添加创建Car的新实例的建造方法。
CarBuilder上的每个setter风格方法返回自身的实例(CarBuilder)。这允许你进行方法链式调用并为对象创建提供流畅的 API。让我们看看它如何使用。
Car muscleCar = Car.builder().make("Ford")
.model("mustang")
.bodyType("coupe")
.build();
现在只使用make和model创建Car比之前更简洁了。只需在Car上简单的调用生成的builder方法获取CarBuilder实例,然后调用任何我们感兴趣的setter风格方法。最后,调用build创建Car的新实例。
另一个值得一提的方便的注解是@Singular。默认情况下,Lombok 为集合创建使用集合参数的标准的 setter 风格方法。在下面的例子中,我们创建了新的 Car并设置了服务日期列表。
Car muscleCar = Car.builder().make("Ford")
.model("mustang")
.serviceDate(Arrays.asList(LocalDate.of(2016, 5, 4)))
.build();
向集合成员变量添加@Singular将提供一个额外的方法,允许你向集合添加单个项。
@Builder
public class Car {
private String make;
private String model;
private String bodyType;
private int yearOfManufacture;
private int cubicCapacity;
@Singular
private List<LocalDate> serviceDate;
}
现在我们可以添加单个服务日期,如下所示:
Car muscleCar3 = Car.builder()
.make("Ford")
.model("mustang")
.serviceDate(LocalDate.of(2016, 5, 4))
.build();
这是一个有助于在创建对象期间处理集合时保持代码简洁的快捷方法。
5.日志
Lombok另一个伟大的功能是日志记录器。如果没有Lombok,要实例化标准的 SLF4J日志记录器,通常会有以下内容:
public class SomeService {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
public void doStuff(){
log.debug("doing stuff....");
}
}
这些日志记录器很沉重,并为每个需要日志记录的类添加了不必要的混乱。值得庆幸的是 Lombok提供了一个为你创建日志记录器的注解。你要做的所有事情就是在类上添加注解,这样就可以了。
@Slf4j
public class SomeService {
public void doStuff(){
log.debug("doing stuff....");
}
}
我在这里使用了@SLF4J注解,但Lombok能为几乎所有通用Java日志框架生成日志记录器。有关更多日志记录器的选项,请参阅文档。
6.Lombok给你控制权
我非常喜欢Lombok的一点是它的不侵入性。。如果你决定在使用如@Getter、@Setter 或 @ToString时也想要自己的方法实现,你的方法将总是优先于 Lombok。它允许你在大多数时间使用Lombok,但在你需要的时候仍有控制权。
7.写得更少,做得更多
在过去的 4 到 5 年里,我几乎在每个项目中都使用了Lombok。我喜欢它,因为它减少了杂乱,最终得到了更干净、更简洁、更易阅读的代码。它不一定为你节省大量时间,因为它生成的代码可以由 IDE 自动生成。话虽如此,我认为更干净的代码的好处不仅仅是将其添加到Java堆栈中。
8. 延展阅读
我已经介绍了我经常使用的Lombok功能,但还有很多我没有讲到。如果你喜欢目前为止所看到的,并希望了解更多,请继续阅读 Lombok 文档。
作者:Brian Hannaway
译者:Darren Luo
推荐关注公众号:锅外的大佬
每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长!
Lombok简介的更多相关文章
- Lombok简介及入门使用 (转载)
Lombok简介及入门使用 lombok既是一个IDE插件,也是一个项目要依赖的jar包. Intellij idea开发的话需要安装Lombok plugin,同时设置 Setting -> ...
- Lombok简介、使用、工作原理、优缺点
1.Lombok简介官方介绍 Project Lombok is a java library that automatically plugs into your editor and build ...
- Lombok介绍及使用方法
lombok简介 lombok是暑假来到公司实习的时候发现的一个非常好用的小工具,刚见到的时候就感觉非常惊艳,有一种相见恨晚的感觉,用了一段时间之后感觉的确挺不错,所以特此来推荐一下. lombok的 ...
- lombok的简单介绍和使用方法
这是上周在群里发现有人推荐lombok,他说是神器,当时就引起了我的好奇,然后下班回来我就看了看官网介绍(菜鸟英语水平),这就是难点了,然后就是大概了解了一下,就在网上查了查相关资料,周末的时候自己试 ...
- Lombok 使用小结
Lombok 简介 Lombok 是一种 Java 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO).它通过注释实现这一目的.通过在开发环境中实现 Lo ...
- Java开发速度神器Lombok,Eclipse端安装使用教程
一.Lombok简介 Lombok是一个代码生成器,可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法. 使用 ...
- SpringBoot(四)SpringBoot中lombok使用
lombok概述 lombok简介 Lombok想要解决了的是在我们实体Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写 ...
- SpringBoot集成Lombok,应用+源码解析,让代码优雅起来
一.Lombok简介 (1)Lombok官网(https://projectlombok.org/)对lombok的介绍 (2)GitHub项目地址:https://github.com/rzwits ...
- lombok的安装
Lombok简介 Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法.官方地址:https:/ ...
随机推荐
- 【Luogu】P2569股票交易(单调队列优化DP)
题目链接 首先这题可以肯定的是朴素DP秒出.然后单调队列优化因为没接触过所以不会emmm 而且脑补没补出来 坐等四月省选倒数第一emmm 心态爆炸,偷懒放题解链接 #include<cstdio ...
- 《学习笔记》Maven
Maven优点之一:jar包统一管理+升级容易+项目清爽 试想一下,我们会在工作中同时创建很多项目,每个项目可能都会引用一些公用的jar包(.NET中是dll文件),一种作法是每个项目里,都复制一份这 ...
- TSP 旅行商问题(状态压缩dp)
题意:有n个城市,有p条单向路径,连通n个城市,旅行商从0城市开始旅行,那么旅行完所有城市再次回到城市0至少需要旅行多长的路程. 思路:n较小的情况下可以使用状态压缩dp,设集合S代表还未经过的城市的 ...
- 转 python基础学习笔记(一)
http://www.cnblogs.com/fnng/category/454439.html 下面我们创建一个文件 root@fnngj-H24X:/hzh/python# touch hell. ...
- Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)
一.spi总线注册 这里所说的SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void).程序如下: 点击(此处 ...
- LeetCode OJ——Longest Valid Parentheses
http://oj.leetcode.com/problems/longest-valid-parentheses/ 最大括号匹配长度,括号是可以嵌套的 #include <string> ...
- Babel6.x的安装过程
1.首先安装babel-cli(用于在终端使用babel) npm install -g babel-cli 2.然后安装babel-preset-es2015插件 npm install --sav ...
- Codeforces 934 A.Compatible Pair
http://codeforces.com/contest/934 A. A Compatible Pair time limit per test 1 second memory limit p ...
- Wannafly挑战赛2 D.Delete(拓扑排序 + dij预处理 + 线段树维护最小值)
题目链接 D.Delete 考虑到原图是个DAG,于是我们可以求出每个点的拓扑序. 然后预处理出起点到每个点的最短路$ds[u]$, 和所有边反向之后从终点出发到每个点的最短路$dt[u]$. 令点 ...
- HDU 5266 pog loves szh III(区间LCA)
题目链接 pog loves szh III 题意就是 求一个区间所有点的$LCA$. 我们把$1$到$n$的$DFS$序全部求出来……然后设$i$的$DFS$序为$c[i]$,$pc[i]$为$c ...