点击左上角,关注:“锅外的大佬”设计模式
专一分享国外最新技术内容
帮助每位开发者更优秀地成长
和其余语言相比, Java常常由于没必要要的冗长被批评。 Lombok提供了一系列注解用以在后台生成模板代码,将其从你的类中删除,从而有助于保持你的代码整洁。较少的模板意味着更简洁的代码,更易于阅读和维护。在本文中,我将涉及我常用的 Lombok功能,并想你展现如何使用他们生产更清晰、更简洁的代码。安全
许多语言经过查看等号右侧的表达式来推断局部变量类型。尽管如今 Java10+已经支持这种功能,但在以前的版本中没有 Lombok的帮助就没法实现。下面的代码段展现了如何显式指定局部类型:框架
final Map < String , Integer > map = new HashMap <>(); map . put ( "Joe" , 21 );
在 Lombok中,咱们能够经过使用 val来缩短它,以下所示:ide
val valMap = new HashMap < String , Integer >(); valMap . put ( "Sam" , 30 );
注意, val在背后建立了一个 final且不可变的变量。若是你须要一个可变本地变量,可使用 var。函数
对方法参数进行 null检查一般不是一个坏主意,特别是若是该方法造成的 API被其余开发者使用。虽然这些检查很简单,可是他们可能变得冗长,特别是当你有多个参数时。以下所示,额外的代码无助于可读性,而且可能从方法的主要目的分散注意力。ui
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检查。this
public void nonNullDemo ( @NonNull Employee employee , @NonNull Account account ) { // just do stuff }
默认状况下, Lombok会抛出 NullPointerException,若是你愿意,能够配置 Lombok抛出 IllegalArgumentException。我我的更喜欢 IllegalArgumentException,由于我认为它更适合于对参数检查。debug
数据类是 Lombok真正有助于减小模板代码的领域。在查看该选项前,思考一下咱们常常须要处理的模板种类。数据类一般包括如下一种或所有:设计
想一想下面的 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 ; }
数据类一般包含一个构造函数,它为每一个成员变量接受参数。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会执行如下操做:
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 ();
这是一个有助于在建立对象期间处理集合时保持代码简洁的快捷方法。
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,但在你须要的时候仍有控制权。
在过去的 4 到 5 年里,我几乎在每一个项目中都使用了 Lombok。我喜欢它,由于它减小了杂乱,最终获得了更干净、更简洁、更易阅读的代码。它不必定为你节省大量时间,由于它生成的代码能够由 IDE 自动生成。话虽如此,我认为更干净的代码的好处不只仅是将其添加到 Java堆栈中。
我已经介绍了我常用的 Lombok功能,但还有不少我没有讲到。若是你喜欢目前为止所看到的,并但愿了解更多,请继续阅读 Lombok 文档。
原文连接:https://dzone.com/articles/introduction-to-lombok
做者:Brian Hannaway
译者:Darren Luo
推荐阅读:避免在Java中检查Null语句上篇好文:Spring Boot实现带STOMP的WebSocket