本文最后更新于:2023-07-01T18:10:40+08:00
简介
Java本身是一种非常强大的语言,但是它有一个缺点,就是过于冗余。在很多情况下,我们按照规范实现一个简单的功能却需要非常多的代码,例如许多的getter、setter等。这些代码与业务逻辑无关,属于样板代码,但是完成起来却是枯燥乏味且消耗时间的。而Lombok可以帮助我们简化这些样板代码。
Lombok的目的是帮助程序员简化样板代码的书写,避免编写重复冗余的样板代码。在Lombok中,提供了许多注解,它们各自有不同的功能。在编译阶段,Lombok会根据这些注解来生成对应的样板代码,添加到.class文件当中,从而简化我们的开发过程。
Lombok的引入可以通过Maven来完成,如下所示。注意这里的作用范围指定为provided
,这是因为我们只在编写代码以及编译阶段会用到该依赖,在编译完成之后,Lombok就已经完成了它的使命。
1 2 3 4 5 6
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> <scope>provided</scope> </dependency>
|
常用注解
Lombok的使用主要就是注解的使用,不同的注解对应不同的简化功能。完整的注解功能可以参考官方文档:projectlombok|features,这里主要结合案例来介绍一些常用的注解。
Getter&Setter
首先是@Getter
和@Setter
注解。该注解辅助生成对应的Getter和Setter。默认情况下生成的是public方法,如果需要指定其他作用范围,则需要在注解的Value值中进行指定,可选值有PUBLIC
、PROTECTED
、PACKAGE
、PRIVATE
等,在AccessLevel
的实现当中。同时需要注意的是,在生成的方法名称中,布尔类型对应的Getter方法名称为isXXX
。
使用Lombok的实现:
1 2 3 4 5 6 7 8 9
| @Getter @Setter public class Worker { int id; @Setter(AccessLevel.PROTECTED) String name; boolean active; }
|
等价Java原始实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class Worker { int id; String name; boolean active;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
protected void setName(String name) { this.name = name; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; } }
|
Constructor
构造器相关的注解包括@NoArgsConstructor
、
@RequiredArgsConstructor
和@AllArgsConstructor
,对应不同参数构造器。
无参构造器以及完整参数构造器容易理解,其中@RequiredArgsConstructor
注解处理的则是那些一定需要初始化的字段,例如使用final修饰的字段,使用@NonNull
修饰的字段等。不过该注解不会处理静态属性字段,
ToString
@ToString注解辅助完成toString
方法的生成。
默认情况下返回的是一个包含类名的字符串,后面跟上每个字段的的名称以及值,使用逗号分隔,类似Worker(id=0, name=null, active=false)
。可以使用includeFieldNames=false
来排除字段名,类似Worker(0, null, false)
。
默认情况下返回字符串会考虑所有非静态字段。可以使用@ToString.Exclude
修饰字段来手动忽略。或者是指定@ToString(onlyExplicitlyIncluded = true)
,然后利用@ToString.Include
来显示指定需要包含的字段。
使用Lombok的实现:
1 2 3 4 5 6
| @ToString public class Worker { private int id; private String name; private boolean active; }
|
等价Java原始实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Worker { private int id; private String name; private boolean active;
@Override public String toString() { return "Worker(" + "id=" + id + ", name=" + name + ", active=" + active + ')'; } }
|
EqualsAndHashCode
@EqualsAndHashCode
注解辅助生成equals()
方法以及hashCode()
方法,默认考虑所有非static以及所有非transient字段,不过也可以参考@ToString
中的字段Include以及Exclude方法来进行手动调整。
Data
@Data
注解的功能类似于上面一系列注解的集合。该注解可以帮助我们直接创建出一个POJO对象,也就是说其中会包括所有字段的Getter、所有非final字段的Setter、涉及类中每个字段的toString、equeals以及hashCode实现,以及所有final字段的构造函数。
使用Lombok的实现:
1 2 3 4 5 6
| @Data public class Worker { int id; String name; boolean active; }
|
Value
@Value
类似于@Data
注解,它会生成相关的方法,但是不包括Setter方法;此外它会将所有属性都看作不可变的final类型,同时使用private进行修饰;同时类本身也会被标注为final。
使用Lombok的实现:
1 2 3 4 5 6
| @Value public class Worker { int id; String name; boolean active; }
|
NonNull
@NonNull
可以帮助生成对应的空检查语句。需要注意的是,该注解只能出现在可能为null的类型上,例如String,而不能修饰Primitive
type,例如int、boolean上,为了解决这个问题,我们可以使用包装类代替原始类型。
使用Lombok的实现:
1 2 3 4 5 6 7 8 9 10 11
| public class Worker { private int id; private String name; private boolean active;
public Worker(int id, @NonNull String name, boolean active) { this.id = id; this.name = name; this.active = active; } }
|
等价Java原始实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Worker { private int id; private String name; private boolean active;
public Worker(int id, String name, boolean active) { this.id = id; if(name == null) { throw new NullPointerException("name is marked non-null but is null"); } this.name = name; this.active = active; } }
|
高级注解
Cleanup
@Cleanup
注解能够确保资源的关闭方法被调用。默认情况下,资源的关闭方法为close()
,不过也可以在注解中进行指定。该注解背后使用的是try-with-resource表达方式来完成的。
Synchronized
@Synchronized
可以帮助我们生成类似synchronized功能的代码。默认情况下普通方法对$lock
对象加锁、静态方法对$LOCK
对象加锁。当然我们也可以在注解Value中可以指定需要加锁的对象。
使用Lombok的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class SynchronizedDemo { private final Object objectToLock = new Object(); @Synchronized public static void sayHello() { System.out.println("Hello!"); } @Synchronized public int getOne() { return 1; } @Synchronized("objectToLock") public void printObject() { System.out.println(objectToLock); } }
|
等价Java原始实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class SynchronizedDemo { private static final Object $LOCK = new Object[0]; private final Object $lock = new Object[0]; private final Object readLock = new Object(); public static void sayHello() { synchronized($LOCK) { System.out.println("Hello"); } } public int getOne() { synchronized($lock) { return 1; } } public void printObject() { synchronized(readLock) { System.out.println(objectToLock); } } }
|
SneakyThrows
对于一个方法来说,如果它会抛出异常,那么它要么需要在方法内部使用try-catch进行异常处理,要么需要在方法后面声明异常抛出,否则将无法通过编译。而@SneakyThrows
会抛出所有受检异常,我们只需要使用该注解,而不需要使用上面的解决方案。
使用Lombok的实现:
1 2 3 4
| @SneakyThrows public void throwException() { throw new Throwable(); }
|
等价Java原始实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void throwException() throws Throwable { throw new Throwable(); }
public void throwException() { try { throw new Throwable(); } catch (Throwable e) { throw new RuntimeException(e); } }
|
Builder
@Builder
可以帮助我们方便的实现一个build模式的对象,添加该注解之后,我们就可以使用相关的构建方法来完成对象的构建。
使用Lombok的实现:
1 2 3 4 5 6
| @Builder public class Worker { int id; String name; boolean active; }
|
添加注解之后,Lombok会辅助生成一个Builder内部类,以及多个相关方法,包括builder()
、build()
等,之后可以使用如下方法来构建对象。
1 2
| Worker.WorkerBuilder builder = Worker.builder(); Worker worker = Worker.builder().id(1).name("name").active(true).build();
|
Log
绝大多数的日志框架在使用之前都需要我们在类中声明一个static
final的日志记录器类Logger。使用@Log
能够帮助我们简化这一声明,Lombok会自动帮助创建一个日志记录器对象,名称为log
。在lombok.extern
中提供了多种不同的日志注解,用于适配不同的日志框架,我们可以根据实际情况来使用不同的注解。
参考文章
- A
Complete Guide to Lombok
- Introduction to
Project Lombok | Baeldung