和其余语言相比,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
类。当生成getter
和setter
时,咱们会获得接近 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
能够替你生成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
推荐关注公众号:锅外的大佬
每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长!
