Lombok 简介
点击左上角,关注:“锅外的大佬”
专注分享国外最新技术内容帮助每位开发者更优秀地成长
和其他语言相比,
Java
经常因为不必要的冗长被批评。 Lombok
提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁。较少的模板意味着更简洁的代码,更易于阅读和维护。在本文中,我将涉及我经常使用的 Lombok
功能,并想你展示如何使用他们生产更清晰、更简洁的代码。1.局部变量类型推断:val 和 var
许多语言通过查看等号右侧的表达式来推断局部变量类型。尽管现在
Java10+
已经支持这种功能,但在之前的版本中没有 Lombok
的帮助就无法实现。下面的代码段展示了如何显式指定局部类型:finalMap<String,Integer> map =newHashMap<>();
map.put("Joe",21);
在
Lombok
中,我们可以通过使用 val
来缩短它,如下所示:val valMap =newHashMap<String,Integer>();
valMap.put("Sam",30);
注意,
val
在背后创建了一个 final
且不可变的变量。如果你需要一个可变本地变量,可以使用 var
。2.@NonNull
对方法参数进行
null
检查通常不是一个坏主意,特别是如果该方法形成的 API
被其他开发者使用。虽然这些检查很简单,但是他们可能变得冗长,特别是当你有多个参数时。如下所示,额外的代码无助于可读性,并且可能从方法的主要目的分散注意力。publicvoid nonNullDemo(Employee employee,Account account){
if(employee ==null){
thrownewIllegalArgumentException("Employee is marked @NonNull but is null");
}
if(account ==null){
thrownewIllegalArgumentException("Account is marked @NonNull but is null");
}
// do stuff
}
理想情况下,你需要
null
检查——没有干扰的那种。这就是 @NonNull
发挥作用的地方。通过用 @NonNull
标记参数, Lombok
替你为该参数生成 null
检查。你的方法突然变得更加简洁,但没有丢失那些安全性的 null
检查。publicvoid nonNullDemo(@NonNullEmployee employee,@NonNullAccount 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 个成员变量的类。publicclassCar{
privateString make;
privateString model;
privateString bodyType;
privateint yearOfManufacture;
privateint cubicCapacity;
publicString getMake(){
return make;
}
publicvoid setMake(String make){
this.make = make;
}
publicString getModel(){
return model;
}
publicvoid setModel(String model){
this.model = model;
}
publicString getBodyType(){
return bodyType;
}
publicvoid setBodyType(String bodyType){
this.bodyType = bodyType;
}
publicint getYearOfManufacture(){
return yearOfManufacture;
}
publicvoid setYearOfManufacture(int yearOfManufacture){
this.yearOfManufacture = yearOfManufacture;
}
publicint getCubicCapacity(){
return cubicCapacity;
}
publicvoid setCubicCapacity(int cubicCapacity){
this.cubicCapacity = cubicCapacity;
}
}
Lombok
可以替你生成 getter
和 setter
模板。通过对每个成员变量使用 @Getter
和 @Setter
注解,你最终得到一个等效的类,如下所示:publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
}
注意,你可以在非
final
成员变量上只使用 @Setter
。在 final
成员变量上使用它将导致编译错误。如果你需要为每个成员变量生成
getter
和 setter
,你也可以在类级别使用 @Getter
和 @Setter
,如下所示。@Getter
@Setter
publicclassCar{
privateString make;
privateString model;
privateString bodyType;
privateint yearOfManufacture;
privateint cubicCapacity;
}
3.2. @AllArgsConstructor
数据类通常包含一个构造函数,它为每个成员变量接受参数。IDE 为
Car
生成的构造函数如下所示:publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
publicCar(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
publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
}
还有其他选项用于生成构造函数。
@RequiredArgsConstructor
将创建带有每个 final
成员变量参数的构造函数, @NoArgsConstructor
将创建没有参数的构造函数。3.3. @ToString
在你的数据类上覆盖
toString
方法是有助于记录日志的良好实践。IDE 为 Car
类生成的 toString
方法如下所示:@AllArgsConstructor
publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
@Override
publicString toString(){
return"Car [make="+ make +", model="+ model +", bodyType="+ bodyType +", yearOfManufacture="
+ yearOfManufacture +", cubicCapacity="+ cubicCapacity +"]";
}
}
我们可以使用
ToString
注解废除这个,如下所示:@ToString
@AllArgsConstructor
publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
}
默认情况下,
Lombok
生成包含所有成员变量的 toString
方法。可以通过 exclude
属性 @ToString(exclude={"someField"},"someOtherField"})
覆盖行为将某些成员变量排除。3.4. @EqualsAndHashCode
如果你正在将你的数据类和任何类型的对象比较,则需要覆盖
equals
和 hashCode
方法。对象的相等是基于业务规则定义的。举个例子,在 Car
类中,如果两个对象有相同的 make
、 model
和 bodyType
,我可能认为他们是相等的。如果我使用 IDE 生成 equals
方法检查 make
、 model
和 bodyType
,它看起来会是这样:@Override
publicboolean equals(Object obj){
if(this== obj)
returntrue;
if(obj ==null)
returnfalse;
if(getClass()!= obj.getClass())
returnfalse;
Car other =(Car) obj;
if(bodyType ==null){
if(other.bodyType !=null)
returnfalse;
}elseif(!bodyType.equals(other.bodyType))
returnfalse;
if(make ==null){
if(other.make !=null)
returnfalse;
}elseif(!make.equals(other.make))
returnfalse;
if(model ==null){
if(other.model !=null)
returnfalse;
}elseif(!model.equals(other.model))
returnfalse;
returntrue;
}
等价的
hashCode
实现如下所示:@Override
publicint hashCode(){
finalint 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"})
publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint 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"})
publicclassCar{
@Getter@Setter
privateString make;
@Getter@Setter
privateString model;
@Getter@Setter
privateString bodyType;
@Getter@Setter
privateint yearOfManufacture;
@Getter@Setter
privateint cubicCapacity;
}
通过使用
@Data
,我们可以将上面的类精简如下:@Data
publicclassCar{
privateString make;
privateString model;
privateString bodyType;
privateint yearOfManufacture;
privateint cubicCapacity;
}
4. 使用 @Buidler 创建对象
建造者设计模式描述了一种灵活的创建对象的方式。
Lombok
可以帮你轻松的实现该模式。看一个使用简单 Car
类的示例。假设我们希望可以创建各种 Car
对象,但我们希望在创建时设置的属性具有灵活性。@AllArgsConstructor
publicclassCar{
privateString make;
privateString model;
privateString bodyType;
privateint yearOfManufacture;
privateint cubicCapacity;
privateList<LocalDate> serviceDate;
}
假设我们要创建一个
Car
,但只想设置 make
和 model
。在 Car
上使用标准的全参数构造函数意味着我们只提供 make
和 model
并设置其他参数为 null
。Car2 car2 =newCar2("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
publicclassCar{
privateString make;
privateString model;
privateString bodyType;
privateint yearOfManufacture;
privateint cubicCapacity;
@Singular
privateList<LocalDate> serviceDate;
}
现在我们可以添加单个服务日期,如下所示:
Car muscleCar3 =Car.builder()
.make("Ford")
.model("mustang")
.serviceDate(LocalDate.of(2016,5,4))
.build();
这是一个有助于在创建对象期间处理集合时保持代码简洁的快捷方法。
5.日志
Lombok
另一个伟大的功能是日志记录器。如果没有 Lombok
,要实例化标准的 SLF4J
日志记录器,通常会有以下内容:publicclassSomeService{
privatestaticfinal org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
publicvoid doStuff(){
log.debug("doing stuff....");
}
}
这些日志记录器很沉重,并为每个需要日志记录的类添加了不必要的混乱。值得庆幸的是
Lombok
提供了一个为你创建日志记录器的注解。你要做的所有事情就是在类上添加注解,这样就可以了。@Slf4j
publicclassSomeService{
publicvoid 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 文档。原文链接:https://dzone.com/articles/introduction-to-lombok作者:Brian Hannaway译者:Darren Luo
推荐阅读:避免在Java中检查Null语句
点击在看,和我一起帮助更多开发者!
阅读原文 最新评论
推荐文章
作者最新文章
你可能感兴趣的文章
Copyright Disclaimer: The copyright of contents (including texts, images, videos and audios) posted above belong to the User who shared or the third-party website which the User shared from. If you found your copyright have been infringed, please send a DMCA takedown notice to [email protected]. For more detail of the source, please click on the button "Read Original Post" below. For other communications, please send to [email protected].
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。
版权声明:以上内容为用户推荐收藏至CareerEngine平台,其内容(含文字、图片、视频、音频等)及知识版权均属用户或用户转发自的第三方网站,如涉嫌侵权,请通知[email protected]进行信息删除。如需查看信息来源,请点击“查看原文”。如需洽谈其它事宜,请联系[email protected]。