和其余语言相比, Java常常由于没必要要的冗长被批评。 Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁。较少的模板意味着更简洁的代码,更易于阅读和维护。在本文中,我将涉及我常用的 Lombok功能,并向你展现如何使用他们生产更清晰、更简洁的代码。java
对方法参数进行 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,由于我认为它更适合于对参数检查。框架
数据类是 Lombok 真正有助于减小模板代码的领域。在查看该选项前,思考一下咱们常常须要处理的模板种类。数据类一般包括如下一种或所有:ide
能够经过 IDE 生成以上内容,所以问题不在于编写他们花费的时间。问题是带有少许成员变量的简单类很快会变得很是冗长。让咱们看看 Lombok 如何经过处理上述的每一项来减小混乱。函数
想一想下面的 Car 类。当生成 getter 和 setter 时,咱们会获得接近 50 行代码来描述一个包含 5 个成员变量的类。ui
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注解,你最终获得一个等效的类,以下所示:this
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成员变量上使用将致使编译错误。spa
若是你须要为每一个成员变量生成 getter和 setter,你也能够在类级别使用 @Getter和 @Setter,以下所示。debug
@Getter @Setter public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; }
数据类一般包含一个构造函数,它为每一个成员变量接受参数。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
将建立没有参数的构造函数。
在你的数据类上覆盖 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"})
覆盖行为将某些成员变量排除。
若是你正在将你的数据类和任何类型的对象比较,则须要覆盖 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。
若是你想使数据类尽量精简,可使用 @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; }
建造者设计模式描述了一种灵活的建立对象的方式。 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会执行如下操做:
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 并设置了服务日期列表。
@Builder public class Car { private String make; private String model; private String bodyType; private int yearOfManufacture; private int cubicCapacity; @Singular private List<LocalDate> serviceDate; }
向集合成员变量添加 @Singular 将提供一个额外的方法,容许你向集合添加单个项。
Car muscleCar3 = Car.builder() .make("Ford") .model("mustang") .serviceDate(LocalDate.of(2016, 5, 4)) .build();
如今咱们能够添加单个服务日期,以下所示:
Car muscleCar3 = Car.builder() .make("Ford") .model("mustang") .serviceDate(LocalDate.of(2016, 5, 4)) .build();
这是一个有助于在建立对象期间处理集合时保持代码简洁的快捷方法。
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日志框架生成日志记录器。有关更多日志记录器的选项,请参阅文档。
我很是喜欢 Lombok 的一点是它的不侵入性。若是你决定在使用如 @Getter、 @Setter 或 @ToString 时也想要本身的方法实现,你的方法将老是优先于 Lombok。它容许你在大多数时间使用 Lombok,但在你须要的时候仍有控制权。