文章目录
- 一、准备
- 二、原理
- 三、使用
- 1. @val
- 2. @var
- 3. @NonNull(判空)
- 4. @Cleanup(自动资源关闭)
- 5. @Getter和@Setter(生成Getter和Setter)
- 6. @ToString(生成ToString)
- 7. @EqualsAndHashCode
- 8. @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
- 9. @Data(5,6,7,8的集合)
- 10. @Value(final版的@Data)
- 11. @Builder(构建者模式)
- 12. @SneakyThrows(自动捕获受检异常)
- 13. @Synchronized(自动加锁)
- 14. @With(final字段赋值)
- 15. @Getter(lazy = true)(将字段值放入缓存)
- 16. @Log(自动生成日志对象)
Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。本文主要介绍lombok的使用。
以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样的代码既没有技术含量,又影响着代码的美观。
一、准备
使用Lombok需要的开发环境Java+Maven+IntelliJ IDEA并安装 Lombok 插件
1. 添加依赖
maven仓库Lombok依赖:https://mvnrepository.com/artifact/org.projectlombok/lombok
例如,1.18.8版本:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
2. IDEA中添加插件
在 Settings --> Plugins 中搜索 Lombok 安装即可,安装完毕后重启IDEA。使用Lombok必须要添加插件。
二、原理
自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。
Lombok就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:
-
javac对源代码进行分析,生成一棵抽象语法树(AST)
-
javac编译过程中调用实现了JSR 269的Lombok程序
-
此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树 (AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点
-
javac使用修改后的抽象语法树(AST)生成字节码文件
更为详细的原理:Java奇技淫巧-插件化注解处理API(Pluggable Annotation Processing API)
三、使用
1. @val
@val
用于声明修饰符为final
的局部变量类型,使用的时候不需要编写实际的类型,编译器将从设定的初始值推断出变量类型。
注意:
- 此功能仅适用于
局部变量
和foreach循环
,而不适用于字段。 - 被修饰的局部变量必须设定初始值
(1)使用Lombok
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;
public class ValExample {
public String example() {
val example = new ArrayList<String>();
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
val map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (val entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
(2)未使用Lombok
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ValExample {
public String example() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
final String foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
final HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (final Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
2. @var
@var的功能类似于@val,不过@var修饰的是非final修饰的局部变量。
3. @NonNull(判空)
@NonNull的作用是,使方法或构造函数中的参数生成一个空检查语句。基本格式是:if (param == null) throw new NullPointerException("param is marked @NonNull but is null");
。
注意:
- 空检查将插入到方法的最顶部。
- 对于构造函数,将在任何显式调用this()或super()之后立即插入空检查。
- 如果顶部已经存在空检查,则不会生成其他空检查。
(1)使用Lombok
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
(2)未使用Lombok
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
4. @Cleanup(自动资源关闭)
使用@Cleanup修饰的变量,可以在变量退出当前作用域范围之前,自动清除给定资源。使用方法:
@Cleanup InputStream in = new FileInputStream("some/file");
这样,将在变量作用域的末尾调用in.close()
方法。该调用会通过try / finally运行,并进行判空。
注意:
-
如果您要清除的对象类型没有close()方法,而是其他无参数方法,则可以像下面这样指定该方法的名称:
@Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);
-
默认情况下,假定清除方法为close()。@Cleanup不能调用带有1个或多个参数的清理方法。
(1)使用Lombok
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
(2)未使用Lombok
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
5. @Getter和@Setter(生成Getter和Setter)
使用@Getter
和@Setter
修饰任何字段,lombok
会自动生成默认的getter
/ setter
。
注意:
- 默认的getter命名:字段名为foo时,getter命名为getFoo,如果字段的类型为boolean时,方法名为isFoo
- 默认的setter:当字段名为foo时,setter的方法名为setFoo,返回值默认为void,形参只有一个,类型与字段相同。
- 默认生成的方法的访问权限为
public
,可以指定一个AccessLevel(见例子中最后一个字段),访问级别PUBLIC,PROTECTED,PACKAGE,PRIVATE;NONE为禁用对应的getter或者setter的生成。 - 在类上添加@Getter或@Setter注释,表示该类中的所有非静态字段均生成getter和setter。
- lombok v1.12.0中的新增功能:字段上的javadoc将被复制到生成的getter和setter中。通常情况下,所有的文字都会被复制,且@return被移动到getter,而@param被移动到setter。
(1)使用Lombok
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
* @return The current value of this person's age. Circles are round.
*/
@Getter
@Setter
private int age = 10;
/**
* Name of the person.
* -- SETTER --
* Changes the name of this person.
*
* @param name The new value.
*/
@Setter(AccessLevel.PROTECTED)
private String name;
@Override
public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
(2)未使用Lombok
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*/
private int age = 10;
/**
* Name of the person.
*/
private String name;
@Override
public String toString() {
return String.format("%s (age: %d)", name, age);
}
/**
* Age of the person. Water is wet.
*
* @return The current value of this person's age. Circles are round.
*/
public int getAge() {
return age;
}
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
*/
public void setAge(int age) {
this.age = age;
}
/**
* Changes the name of this person.
*
* @param name The new value.
*/
protected void setName(String name) {
this.name = name;
}
}
6. @ToString(生成ToString)
使用@ToString修饰类,生成该类的toString()方法。默认情况下,它将按顺序打印Class名称以及每个字段的名称,并以逗号分隔。
-
将
includeFieldNames
参数设置为true(默认为false),toString()方法中会额外增加每个字段的字段名。 -
默认情况下,将打印所有非静态字段。如果要跳过某些字段,可以用
@ToString.Exclude
注释这些字段。另外,可以使用@ToString(onlyExplicitlyIncluded = true)
精确指定要打印的字段,需要使用@ToString.Include
标记每个要打印的字段。 -
通过设置
callSuper
为true
,可以将超类实现的 toString 包含到输出中。 -
可以在 toString 中包含不带参数的实例(非静态)方法。使用
@ToString.Include
标记方法即可。 -
@ToString.Include(name = "some other name")
可以用来更改 toString 方法中输出时字段的名称, -
@ToString.Include(rank = -1)
可以更改成员的打印顺序。没有指定等级的成员默认为 0,等级更高的成员首先被打印,等级相同的成员以它们在源文件中出现的顺序打印。
(1)使用Lombok
import lombok.ToString;
@ToString
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
@ToString.Exclude
private int id;
public String getName() {
return this.name;
}
@ToString(callSuper = true, includeFieldNames = true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
(2)未使用Lombok
import java.util.Arrays;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public String toString() {
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
}
}
@Override
public String toString() {
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
}
}
7. @EqualsAndHashCode
使用@EqualsAndHashCode对任何类定义进行注释,可以使lombok生成equals(Object other)
和hashCode()
方法的实现。默认情况下,它将使用所有非静态(static)和非瞬态(transient)字段
- 与 @ToString 类似,使用
@EqualsAndHashCode.Include
或@EqualsAndHashCode.Exclude
来标记包含哪些字段和排除哪些字段;使用@EqualsAndHashCode(onlyExplicitlyIncluded = true)
只包含用@EqualsAndHashCode.Include
修饰的字段 - 如果被@EqualsAndHashCode修饰的类继承了java.lang.Object除外其它类,则需要设置
@EqualsAndHashCode(callSuper=true)
。换句话说,@EqualsAndHashCode
生成的 euqals 方法不会比较其父类,需要加入参数callSuper=true
- 除非类是
final
的,否则lombok会生成一个canEqual
方法
(1)使用Lombok
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
@EqualsAndHashCode.Exclude
private Shape shape = new Square(5, 10);
private String[] tags;
@EqualsAndHashCode.Exclude
private int id;
public String getName() {
return this.name;
}
@EqualsAndHashCode(callSuper = true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
(2)未使用Lombok
import java.util.Arrays;
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeExample)) return false;
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
if (!other.canEqual((Object) this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (Double.compare(this.score, other.score) != 0) return false;
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
return true;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.score);
result = (result * PRIME) + (this.name == null ? 43 : this.name.hashCode());
result = (result * PRIME) + (int) (temp1 ^ (temp1 >>> 32));
result = (result * PRIME) + Arrays.deepHashCode(this.tags);
return result;
}
protected boolean canEqual(Object other) {
return other instanceof EqualsAndHashCodeExample;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Square)) return false;
Square other = (Square) o;
if (!other.canEqual((Object) this)) return false;
if (!super.equals(o)) return false;
if (this.width != other.width) return false;
if (this.height != other.height) return false;
return true;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result * PRIME) + super.hashCode();
result = (result * PRIME) + this.width;
result = (result * PRIME) + this.height;
return result;
}
protected boolean canEqual(Object other) {
return other instanceof Square;
}
}
}
8. @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
这组的3个注解会生成一个构造函数,该构造函数将为某些字段赋值。
-
@NoArgsConstructor
将生成没有参数的构造函数。如果不通生成(由于final字段),则将导致编译器错误,除非使用@NoArgsConstructor(force = true)
,初始化所有final字段 。对于具有约束的字段(例如,@NonNull字段),不会生成检查。该注释主要与@Data其他生成注释的构造函数之一或其中一个结合使用。 -
@RequiredArgsConstructor
为每个需要处理的字段生成一个带有1个参数的构造函数。所有未初始化的字段(包括final字段和@NonNull声明的字段)均会获取参数。对于标记为的字段@NonNull,还将生成一个显式的null检查。参数的顺序与字段在类中出现的顺序匹配。 -
@AllArgsConstructor
为类生成一个带有类中所有字段的构造函数。标记为@NonNull
的字段将进行空检查。 -
通过
@RequiredArgsConstructor(staticName="of")
来使用备用格式(以上三种注解均可使用),其中生成的构造函数是私有的,并生成基于私有构造函数的静态工厂方法。与常规构造函数不同,这种静态工厂方法将推断泛型。
(1)使用Lombok
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
(2)未使用Lombok
public class ConstructorExample<T> {
private int x, y;
@NonNull
private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull
private String field;
public NoArgsExample() {
}
}
}
9. @Data(5,6,7,8的集合)
@Data
是@ToString
,@EqualsAndHashCode
,@Getter/@Setter
和@RequiredArgsConstructor
的集合。即@Data生成与简单的POJO(Plain Old Java Object)和beans相同的模板:所有字段的getter,所有非final字段的setter和toString,equals以及hashCode,以及一个构造函数。
-
在类上使用
@Data
,就像隐式的使用@Getter
,@Setter
,@ToString
,@EqualsAndHashCode
和@RequiredArgsConstructor
,如果要设置这些注解的参数(如callSuper,includeFieldNames和exclude)时,不能设置@Data
,而是显式添加要设置的注解即可;@Data非常聪明,可以遵循这些注释。 -
所有生成的getter和setter将为public。
-
如果实体类中存在
getter
和setter
方法,然后又使用了@Data
注解,则@Data
不会覆盖实体类中已经存在的getter
和setter
-
被transient修饰的所有字段不会加入hashCode和equals。
-
所有静态字段都完全跳过(任何生成的方法都不会考虑,并且不会为它们创建setter / getter)。
-
特别注意:生成的
equals
方法不会比较其父类,即不会调用父类的euqals
方法,如果父类为Object
对象则不用管
(1)使用Lombok
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
@Data
public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE)
private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames = true)
@Data(staticConstructor = "of")
public static class Exercise<T> {
private final String name;
private final T value;
}
}
(2)未使用Lombok
import java.util.Arrays;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
public DataExample(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setScore(double score) {
this.score = score;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
@Override
public String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
DataExample other = (DataExample) o;
if (!other.canEqual((Object) this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
result = (result * PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result * PRIME) + this.getAge();
result = (result * PRIME) + (int) (temp1 ^ (temp1 >>> 32));
result = (result * PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@Override
public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Exercise)) return false;
Exercise<?> other = (Exercise<?>) o;
if (!other.canEqual((Object) this)) return false;
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName()))
return false;
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue()))
return false;
return true;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result * PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result * PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
}
}
10. @Value(final版的@Data)
@Value
与@Data
类似;@Value
修饰的是不可变类型(final
),默认情况下类本身是final
修饰的,所有字段由private
和final
修饰,每个字段会生成gettter
的方法,生成一个覆盖每个参数(除了在字段声明时已经初始化了的final类型字段)的构造方法,toString()
,equals()
和hashCode()
均会被生成,不会产生setter方法。
@Value相当于是:final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter
的简写
(1)使用Lombok
import lombok.AccessLevel;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.experimental.Wither;
import lombok.ToString;
@Value
public class ValueExample {
String name;
@Wither(AccessLevel.PACKAGE)
@NonFinal
int age;
double score;
protected String[] tags;
@ToString(includeFieldNames = true)
@Value(staticConstructor = "of")
public static class Exercise<T> {
String name;
T value;
}
}
(2)未使用Lombok
import java.util.Arrays;
public final class ValueExample {
private final String name;
private int age;
private final double score;
protected final String[] tags;
@java.beans.ConstructorProperties({"name", "age", "score", "tags"})
public ValueExample(String name, int age, double score, String[] tags) {
this.name = name;
this.age = age;
this.score = score;
this.tags = tags;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample)) return false;
final ValueExample other = (ValueExample) o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.getAge();
final long $score = Double.doubleToLongBits(this.getScore());
result = result * PRIME + (int) ($score >>> 32 ^ $score);
result = result * PRIME + Arrays.deepHashCode(this.getTags());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
}
ValueExample withAge(int age) {
return this.age == age ? this : new ValueExample(name, age, score, tags);
}
public static final class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample.Exercise)) return false;
final Exercise<?> other = (Exercise<?>) o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
final Object this$value = this.getValue();
final Object other$value = other.getValue();
if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
final Object $value = this.getValue();
result = result * PRIME + ($value == null ? 43 : $value.hashCode());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
}
}
}
11. @Builder(构建者模式)
@Builder
的作用域是类,使用此注解后类中新增一个成员类(Builder
)将会使用构建者模式,编译时增加了一个Builder
内部类和全字段的构造器。@Builder.Default
用于指定Builder中的属性的默认值,@Singular
用于告诉lombok当前属性类型是集合类型,lombok会为信任的集合类型添加"adder"方法而不是"setter"方法。
(1)使用Lombok
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default
private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular
private Set<String> occupations;
}
(2)未使用Lombok
import java.util.Set;
public class BuilderExample {
private long created;
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
Set<String> occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
12. @SneakyThrows(自动捕获受检异常)
@SneakyThrows的作用域是构造或者方法,用于自动捕获(隐藏)检查异常。我们知道,java对于检查异常,需要在编码时进行捕获,或者抛出。该注解的作用是将检查异常包装为运行时异常,那么编码时就无需处理异常了。
提示:不过这并不是友好的编码方式,因为你编写的api的使用者,不能显式的获知需要处理检查异常。
(1)使用Lombok
import lombok.SneakyThrows;
public class SneakyThrowsExample implements Runnable {
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
(2)未使用Lombok
import lombok.Lombok;
public class SneakyThrowsExample implements Runnable {
public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}
public void run() {
try {
throw new Throwable();
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
}
}
}
13. @Synchronized(自动加锁)
@Synchronized
的作用域是方法,用于方法同步,使用此注解后,方法体中的代码块自动包含在一个synchronize
块中。synchronize
块加锁的对象一定是类中的一个成员属性,可以通过@Synchronized
的value
指定,如果不存在则由lombok新建,一般是private final Object $lock = new Object[0];
。
(1)使用Lombok
import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
(2)未使用Lombok
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public static void hello() {
synchronized ($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized ($lock) {
return 42;
}
}
public void foo() {
synchronized (readLock) {
System.out.println("bar");
}
}
}
14. @With(final字段赋值)
用@With
修饰不可变字段(final),通过克隆对象的方式,对对象中不可变字段进行赋值(setter
)
(1)使用Lombok
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.With;
public class WithExample {
@With(AccessLevel.PROTECTED)
@NonNull
private final String name;
@With
private final int age;
public WithExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
}
(2)未使用Lombok
import lombok.NonNull;
public class WithExample {
private @NonNull final String name;
private final int age;
public WithExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
protected WithExample withName(@NonNull String name) {
if (name == null) throw new java.lang.NullPointerException("name");
return this.name == name ? this : new WithExample(name, age);
}
public WithExample withAge(int age) {
return this.age == age ? this : new WithExample(name, age);
}
}
15. @Getter(lazy = true)(将字段值放入缓存)
@Getter(lazy = true)
用来修饰字段,lombok生成一个该字段的getter方法,在第一次调用getter时计算字段的值,然后将其缓存。适用于计算该值占用大量CPU或内存的情况。使用方法:请创建一个private final
变量,使用运行起来很昂贵的表达式对其进行初始化,然后使用@Getter(lazy=true)
注释该字段。
(1)使用Lombok
import lombok.Getter;
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;
}
}
(2)未使用Lombok
public class GetterLazyExample {
private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
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;
}
}
16. @Log(自动生成日志对象)
@Log的作用域是类,用于生成日志API句柄。将@Log
放在类上(适用于您所使用的日志系统的任何一种);然后,lombok会生成一个static final log
字段,该字段按照使用的日志记录框架规定的方式进行初始化,然后可以使用该字段编写日志语句。目前支持的类型如下:
-
@CommonsLog
:private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
-
@Flogger
:private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
-
@JBossLog
:private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
-
@Log
:private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
-
@Log4j
:private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
-
@Log4j2
:private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
-
@Slf4j
:private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
-
@XSlf4j
:private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
-
@CustomLog
:private static final com.foo.your.Logger log = com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
(1)使用Lombok
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@Log
public class LogExample {
public static void main(String... args) {
log.severe("Something's wrong here");
}
}
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
@CommonsLog(topic = "CounterLog")
public class LogExampleCategory {
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
(2)未使用Lombok
public class LogExample {
private static final java.util.logging.Logger log =
java.util.logging.Logger.getLogger(LogExample.class.getName());
public static void main(String... args) {
log.severe("Something's wrong here");
}
}
public class LogExampleOther {
private static final org.slf4j.Logger log =
org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
public class LogExampleCategory {
private static final org.apache.commons.logging.Log log =
org.apache.commons.logging.LogFactory.getLog("CounterLog");
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}