Lombok简介

Lombok简介

和其余语言相比,Java常常由于没必要要的冗长被批评。Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁。较少的模板意味着更简洁的代码,更易于阅读和维护。在本文中,我将涉及我常用的Lombok功能,并想你展现如何使用他们生产更清晰、更简洁的代码。java

1.局部变量类型推断:val 和 var

许多语言经过查看等号右侧的表达式来推断局部变量类型。尽管如今Java 10+已经支持这种功能,但在以前的版本中没有Lombok的帮助就没法实现。下面的代码段展现了如何显式指定局部类型:web

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被其余开发者使用。虽然这些检查很简单,可是他们可能变得冗长,特别是当你有多个参数时。以下所示,额外的代码无助于可读性,而且可能从方法的主要目的分散注意力。bash

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,由于我认为它更适合于对参数检查。ide

##3.更简洁的数据类 数据类是Lombok真正有助于减小模板代码的领域。在查看该选项前,思考一下咱们常常须要处理的模板种类。数据类一般包括如下一种或所有:函数

  • 构造函数(有或没有参数)
  • 私有成员变量的 getter 方法
  • 私有非 final 成员变量的 setter 方法
  • 帮助记录日志的 toString 方法
  • equals 和 hashCode(处理相等/集合)

能够经过 IDE 生成以上内容,所以问题不在于编写他们花费的时间。问题是带有少许成员变量的简单类很快会变得很是冗长。让咱们看看Lombok如何经过处理上述的每一项来减小混乱。ui

3.1. @Getter 和 @Setter

想一想下面的Car类。当生成gettersetter时,咱们会获得接近 50 行代码来描述一个包含 5 个成员变量的类。this

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能够替你生成gettersetter模板。经过对每一个成员变量使用 @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成员变量上使用它将致使编译错误。

若是你须要为每一个成员变量生成gettersetter,你也能够在类级别使用 @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

若是你正在将你的数据类和任何类型的对象比较,则须要覆盖equalshashCode 方法。对象的相等是基于业务规则定义的。举个例子,在Car类中,若是两个对象有相同的makemodelbodyType,我可能认为他们是相等的。若是我使用 IDE 生成equals方法检查makemodelbodyType,它看起来会是这样:

@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排除某些成员变量。在上面的代码片断中。我已经从生成的equalshashCode方法中排除了 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,但只想设置makemodel。在Car上使用标准的全参数构造函数意味着咱们只提供makemodel并设置其余参数为null

Car2 car2 = new Car2("Ford", "Mustang", null, null, null, null); 

这可行但并不理想,咱们必须为咱们不感兴趣的参数传递null。咱们能够建立一个只接受makemodel的构造函数来避开这个问题。这是一个合理的解决方法,但不够灵活。若是咱们有许多不一样的字段排列,咱们能够用什么来建立一个新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(); 

如今只使用makemodel建立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 文档。

原文连接:dzone.com/articles/in…

做者:Brian Hannaway

译者:Darren Luo

推荐关注公众号:锅外的大佬

每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长!

 

相关文章
相关标签/搜索