java如何使用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 文档。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot利用Lombok减少Java中样板代码的方法示例

    前言 Lombok想要解决了的是在我们实体Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写,以期方便使用的方法:在使用Lombok之后,将由其来自动帮你实现代码生成,注意,其是在运行过程中,帮你自动生成的.就是说,将极大减少你的代码总量. Lombok的官方地址: https://projectlombok.org/ 其实说实话第一次听到Lombok的时候,是从一个带我的匈牙利老师傅那里学来的.那个时候他给了我一套J

  • java10下编译lombok注解代码分享

    序 本文主要研究下在带有lombok(1.16.20版本)注解的代码在java10下的编译问题. 问题 Fatal error compiling at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) at org.apa

  • Java开发之Lombok指南

    1. 前言 在目前众多编程语言中,Java 语言的表现还是抢眼,不论是企业级服务端开发,还是 Andorid 客户端开发,都是作为开发语言的首选,甚至在大数据开发领域,Java 语言也能占有一席之地,如 Hadoop,Spark,Flink 大数据等.而作为已经诞生 24 年的 Java 相比其他语言来说,编写起来略显得冗长和复杂,而为了能极大提升 Java 开发的效率和代码简洁性,一个 Java 库 Lombok 就这样诞生了. 首先我们还是看下 Lombok 官方的描述: Project L

  • java项目中使用 Lombok遇到的问题小结

    一.Maven项目使用步骤一般包含两步,1)引入依赖 2)特定的 IDE 引入对应的插件 1)在POM中引入依赖 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version

  • java如何使用Lombok更优雅地编码

    Lombok简介 和其他语言相比,Java经常因为不必要的冗长被批评.Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁.较少的模板意味着更简洁的代码,更易于阅读和维护.在本文中,我将涉及我经常使用的Lombok功能,并想你展示如何使用他们生产更清晰.更简洁的代码. 1.局部变量类型推断:val 和 var 许多语言通过查看等号右侧的表达式来推断局部变量类型.尽管现在Java 10+已经支持这种功能,但在之前的版本中没有Lombok的帮助就无法实现

  • Java中List常用操作比for循环更优雅的写法示例

    目录 引言 简单遍历 筛选符合某属性条件的List集合 获取某属性返回新的List集合 获取以某属性为key,其他属性或者对应对象为value的Map集合 以某个属性进行分组的Map集合 其他情况 总结 引言 使用JDK1.8之后,大部分list的操作都可以使用lambda表达式去写,可以让代码更简洁,开发更迅速.以下是我在工作中常用的lambda表达式对list的常用操作,喜欢建议收藏. 以用户表为例,用户实体代码如下: public class User { private Integer

  • 9个小技巧让你的Java if else看起来更优雅

    前言 if else 是我们写代码时,使用频率最高的关键词之一,然而有时过多的 if else 会让我们感到脑壳疼,例如下面这个伪代码: 是不是很奔溃?虽然他是伪代码,并且看起来也很夸张,但在现实中,当我们无数次 review 别人代码时,都会发现类似的场景,那么我们本文就来详细聊聊,有没有什么方法可以让我们避免来写这么多的 if else 呢? 我们本文提供了 9 种方法来解决掉那些"烦人"的 if else,一起来看吧. 1.使用 return 我们使用 return 去掉多余的

  • 如何更优雅地获取spring boot yml中的值

    前言 偶然看到国外论坛有人在吐槽同事从配置文件获取值的方式,因此查阅了相关资料发现确实有更便于管理更优雅的获取方式. github demo地址: springboot-yml-value 1.什么是yml文件 application.yml取代application.properties,用来配置数据可读性更强,尤其是当我们已经制定了很多的层次结构配置的时候. 下面是一个非常基本的yml文件: server: url: http://localhost myapp: name: MyAppli

  • Java JDK1.7对字符串的BASE64编码解码方法

    如下所示: package cn.itcast; import java.io.IOException; import java.io.UnsupportedEncodingException; import org.junit.Test; import sun.misc.BASE64Decoder; /* * @author soto * BASE64编码 解码 * */ public class Demo1 { @Test public void fun1() throws IOExcept

  • Java开发神器Lombok使用详解

    最近正在写SpringBoot系列文章和录制视频教程,每次都要重复写一些Getter/Setter.构造器方法.字符串输出的ToString方法和Equals/HashCode方法等.甚是浪费时间,也影响代码的可读性.因此,今天就给大家推荐一款Java开发神器--Lombok,让代码更简单易读. 什么是Lombok Lombok是一款Java开发插件,可以通过它定义的注解来精简冗长和繁琐的代码,主要针对简单的Java模型对象(POJO). 好处就显而易见了,可以节省大量重复工作,特别是当POJO

  • java实战小技巧之优雅的实现字符串拼接

    目录 前言 String底层原理 1. 普通写法 2. StringJoiner 3. guava joiner 4. 小结 总结 前言 字符串拼接不管是在业务上,还是写算法时都会频繁使用到. 相信没有小伙伴没有写过这样的代码,比如说现在让我们来实现一个字符串拼接的场景,怎样的实现才算是优雅的呢? 以将int数组转为英文逗号分隔的字符串为例进行演示 String底层原理 在讨论字符串拼接时,首先需要知道String的底层原理. 看下结构 private final byte[] value; 这

  • 如何利用Promises编写更优雅的JavaScript代码

    你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别.难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它们写出更优雅的 JavaScript 代码. Promises 易于阅读 比如说我们想从 HipsterJesus 的API中抓取一些数据并将这些数据添加到我们的页面中.这些 API 的响应数据形式如下: { "text": "<p>Lorem ipsum...<

  • Python使用CMD模块更优雅的运行脚本

    本文实例讲述了Python使用CMD模块更优雅的运行脚本的方法.分享给大家供大家参考.具体分析如下: 平时由于经常给测试人员调试一些东西,虽然写了一些脚本,感觉还是不方便. python的cmd模块提供的更优雅的方式,很喜欢. 刚写了一些平时常用的测试代码: # -*- coding:utf-8 -*- import os, sys from datetime import datetime import cmd import warnings from django.conf import s

  • Go语言中更优雅的错误处理

    从现状谈起 Go语言受到诟病最多的一项就是其错误处理机制.如果显式地检查和处理每个error,这恐怕的确会让人望而却步.下面我们将给大家介绍Go语言中如何更优雅的错误处理. Golang 中的错误处理原则,开发者曾经之前专门发布了几篇文章( Error handling and Go和 Defer, Panic, and Recover.Errors are values )介绍.分别介绍了 Golang 中处理一般预知到的错误与遇到崩溃时的错误处理机制. 一般情况下,我们还是以官方博客中的错误

随机推荐