Lombok 消除冗余代码 发表于 2022-08-17 更新于 2023-06-14
字数总计: 1.9k 阅读时长: 8分钟 阅读量: 上海
前言 Lombok
是一款 Java 开发插件,使得 Java
开发者可以通过其定义的一些注解来消除业务过程中冗余的代码,尤其是简单的 Java
模型对象(POJO
)。而当我们如果在开发环境中使用 Lombok
开发插件后,可以省出重复构建,诸如 hashCode
和 equals
这样的方法以及各种业务对象模型的 accessor
和 ToString
等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生产这些方法,并没有如反射那样降低程序的性能。
可以用来帮助开发人员消除冗余的代码,对于一些简单的 Java
对象(POJO
),它通过注释实现这一目的。
实现原理 Lombok 的实现原理,基于 JSR269(Pluggable Annotation Processing API) 规范,自定义编译器注解处理器,用于在 Javac 编译阶段时,扫描使用到 Lombok 定义的注解的类,进行自定义的代码生成。
安装步骤 在 IDEA 中,已经提供了 IntelliJ Lombok plugin 插件,方便我们使用 Lombok。安装方式很简单,只需要在 IDEA Plugins 功能中,搜索 Lombok 关键字即可。如下图所示:
IDEA 2020.3 版本以上已经内置 Lombok Plugin 插件,无需手动安装
Lombok 常用注解说明
@NonNull
给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出 NPE (NullPointerException
)
@Cleanup
自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成 try-finally
这样的代码来关闭流
@Getter/@Setter
用在属性上,再也不用自己手写 setter 和 getter 方法了,还可以指定访问范围
@ToString
用在类上,可以自动覆写 toString 方法,当然还可以加其他参数,例如@ ToString
(exclude
=”id
”)排除 id 属性,或者@ToString
(callSuper
=true
,includeFieldNames
=true
)调用父类的 toString 方法,包含所有属性
@EqualsAndHashCode
用在类上,自动生成equals
方法和hashCode
方法
@NoArgsConstructor
@RequiredArgsConstructor
and @AllArgsConstructor
:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull 属性作为参数的构造函数,如果指定staticName
= “of”
参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
@Data
注解在类上,相当于同时使用了@ToString
、@EqualsAndHashCode
、@Gette
r、@Setter
和@RequiredArgsConstrutor
这些注解,对于POJO
类十分有用
@Value
用在类上,是@Data
的不可变形式,相当于为属性添加 final 声明,只提供getter
方法,而不提供setter
方法
@Builder
用在类、构造器、方法上,为你提供复杂的builder APIs
,让你可以像如下方式一样调用Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
更多说明参考Builder
@SneakyThrows
自动抛受检异常,而无需显式在方法上使用 throws 语句
@Synchronized
用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock
或$LOCK,
而java
中的synchronized
关键字锁对象是this
,锁在this
或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁 this 或者类对象,这可能会导致竞争条件或者其它线程错误
@Getter(lazy=true)
可以替代经典的Double Check Lock
样板代码
@Log
根据不同的注解生成不同类型的log
对象,但是实例名称都是log
,有六种可选实现类
val
用在局部变量前面,相当于将变量声明为 final
Lombok 代码示范
@Nonnull 为方法和构造函数的参数提供非空检查
1 2 3 4 5 6 7 8 9 10 11 12 TYPESCRIPT public void notNullExample(@NonNull String string) { string.length(); } //=>相当于 public void notNullExample(String string) { if (string != null) { string.length(); } else { throw new NullPointerException("null"); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 TYPESCRIPT public static void main(String[] args) { try { @Cleanup InputStream inputStream = new FileInputStream(args[0]); } catch (FileNotFoundException e) { e.printStackTrace(); } //=>相当于 InputStream inputStream = null; try { inputStream = new FileInputStream(args[0]); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Getter/@Setter 对类的属性字段自动生成 Get/Set 方法
1 2 3 4 5 JAVA @Setter(AccessLevel.PUBLIC) @Getter(AccessLevel.PROTECTED) private int id; private String shap;
@ToString 为类生成一个 toString 方法
1 2 3 4 5 6 7 8 9 10 11 TYPESCRIPT @ToString(exclude = "id", callSuper = true, includeFieldNames = true) public class LombokDemo { private int id; private String name; private int age; public static void main(String[] args) { //输出LombokDemo(super=LombokDemo@48524010, name=null, age=0) System.out.println(new LombokDemo()); } }
@EqualsAndHashCode 为类生成 equals 和 hasCode 方法
1 2 3 4 5 6 TYPESCRIPT @EqualsAndHashCode(exclude = {"id", "shape"}, callSuper = false) public class LombokDemo { private int id; private String shap; }
@NoArgsConstructor,@RequiredArgsConstructor and @AllArgsConstructor,分别为类自动生成无参构造,指定参数构造器和包含所有参数构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 TYPESCRIPT @NoArgsConstructor @RequiredArgsConstructor(staticName = "of") @AllArgsConstructor public class LombokDemo { @NonNull private int id; @NonNull private String shap; private int age; public static void main(String[] args) { new LombokDemo(1, "circle"); //使用静态工厂方法 LombokDemo.of(2, "circle"); //无参构造 new LombokDemo(); //包含所有参数 new LombokDemo(1, "circle", 2); } }
@Data 在类上使用,相当于同时使用@ToString、@EqualsAndHashCode、@Getter、@Setter 和@RequiredArgsConstructor 这些注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 JAVA @Data public class Menu { private String shopId; private String skuMenuId; private String skuName; private String normalizeSkuName; private String dishMenuId; private String dishName; private String dishNum; //默认阈值 private float thresHold = 0; //新阈值 private float newThresHold = 0; //总得分 private float totalScore = 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 JAVA @Value public class LombokDemo { @NonNull private int id; @NonNull private String shap; private int age; //相当于 private final int id; public int getId() { return this.id; } ... }
1 2 3 4 5 6 7 8 9 10 11 TYPESCRIPT @Builder public class BuilderExample { private String name; private int age; @Singular private Set<String> occupations; public static void main(String[] args) { BuilderExample test = BuilderExample.builder().age(11).name("test").build(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 JAVA import lombok.SneakyThrows; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.UnsupportedEncodingException; public class Test { @SneakyThrows() public void read() { InputStream inputStream = new FileInputStream(""); } @SneakyThrows public void write() { throw new UnsupportedEncodingException(); } //相当于 public void read() throws FileNotFoundException { InputStream inputStream = new FileInputStream(""); } public void write() throws UnsupportedEncodingException { throw new UnsupportedEncodingException(); } }
@Synchronized 将方法声明同步并自动加锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 TYPESCRIPT public class SynchronizedDemo { @Synchronized public static void hello() { System.out.println("world"); } //相当于 private static final Object $LOCK = new Object[0]; public static void hello() { synchronized ($LOCK) { System.out.println("world"); } } }
@Getter(lazy=true)可以替代经典的 Double check Lock 样板代码
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 30 31 32 33 34 35 36 37 38 39 40 41 JAVA public class GetterLazyExample { @Getter(lazy = true) private final double[] cached = expensive(); private double[] expensive() { double[] result = new double[1000000]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); } return result; } } // 相当于如下所示: import java.util.concurrent.atomic.AtomicReference; public class GetterLazyExample { private final AtomicReference<java.lang.Object> cached = new AtomicReference<>(); public double[] getCached() { java.lang.Object value = this.cached.get(); if (value == null) { synchronized (this.cached) { value = this.cached.get(); if (value == null) { final double[] actualValue = expensive(); value = actualValue == null ? this.cached : actualValue; this.cached.set(value); } } } return (double[]) (value == this.cached ? null : value); } private double[] expensive() { double[] result = new double[1000000]; for (int i = 0; i < result.length; i++) { result[i] = Math.asin(i); } return result; } }
@Log 根据不同的注解生成不同类型的 log 对象
1 2 3 4 5 6 7 JAVA @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName()); @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class); @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class); @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class); @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
1 2 3 4 5 6 7 8 9 10 DART public static void main(String[] args) { val sets = new HashSet<String>(); val lists = new ArrayList<String>(); val maps = new HashMap<String, String>(); //=>相当于如下 final Set<String> sets2 = new HashSet<>(); final List<String> lists2 = new ArrayList<>(); final Map<String, String> maps2 = new HashMap<>(); }