lombok注解使用说明

曾以为他是生性冷淡,直到看到他为另一个人嘘寒问暖。你才突然明白,这世上根本没有高冷的人,只是他暖的不是你

Posted by yishuifengxiao on 2021-01-19

一 lombok注解说明

val

val是在lombok 0.10中引入的。

您可以将val用作局部变量声明的类型,而不是实际编写该类型。 执行此操作时,将从初始化程序表达式中推断类型。 局部变量也将成为最终变量。 此功能仅适用于局部变量和foreach循环,不适用于字段。 初始化表达式是必需的。

val实际上是某种“类型”,在lombok包中作为真实类存在。 您必须导入它以使val正常工作(或使用lombok.val作为类型)。 本地变量声明中此类型的存在会触发final关键字的添加以及复制覆盖“假” val类型的初始化表达式的类型。

警告:此功能当前在NetBeans中不起作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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());
}
}
}

编译后会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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());
}
}
}

对于复合类型,将推断出最常见的超类,而不是任何共享接口。 例如,bool ? new HashSet():new ArrayList()是具有复合类型的表达式:结果既是AbstractCollection也是Serializable。 推断的类型将是AbstractCollection,因为它是一个类,而Serializable是接口。

在模棱两可的情况下,例如初始化器表达式为null时,将推断java.lang.Object。

@NonNull

@NonNull was introduced in lombok v0.11.10.

您可以在方法或构造函数的参数上使用@NonNull,以使lombok为您生成空检查语句。

Lombok一直将通常在字段上命名为@NonNull的各种注释作为信号来生成空检查,如果lombok通过例如@Data为您生成了整个方法或构造函数。 但是,现在,在参数上使用lombok自己的@ lombok.NonNull会导致在您自己的方法或构造函数中仅插入null-check语句。

空检查看起来像是否(param == null)抛出新的NullPointerException(“ param被标记为@NonNull但为null”); 并将其插入到您方法的最顶部。 对于构造函数,将在任何显式this()或super()调用之后立即插入空检查。

如果顶部已经存在空检查,则不会生成其他空检查。

1
2
3
4
5
6
7
8
9
10
import lombok.NonNull;

public class NonNullExample extends Something {
private String name;

public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}

编译后会在构造器中添加非空判断,具体代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
}
}

Lombok对现有的空检查的检测方案包括扫描看起来像lombok自己的if语句或assert语句。任何if语句的“ then”部分中的“ throws”语句(无论是否用大括号)都重要。对名为requireNonNull或checkNotNull的任何方法的任何调用都将计数。 if语句的条件必须看起来完全像PARAMNAME == null; assert语句必须看起来完全像PARAMNAME!= null。对requireNonNull样式方法的调用必须是单独的(仅调用该方法的语句),或者必须是赋值或变量声明语句的表达式。方法中的第一个不是空检查的语句将停止检查空检查的过程。

虽然@Data和其他生成方法的lombok注释将在表示该字段绝不能为@NonNull的各种众所周知的注释上触发,但此功能仅在lombok包中的lombok自己的@NonNull注释上触发。

基本参数上的@NonNull会导致警告。不会生成空检查。

用于生成警告的抽象方法的参数上的@NonNull;从版本1.16.8开始,不再存在这种情况,以承认@NonNull也具有记录作用的观点。出于同样的原因,您可以将方法注释为@NonNull;。这是允许的,不会产生警告,也不会产生任何代码。

@Cleanup

当我们在Java中使用资源时,不可避免地需要在使用后关闭资源。使用@Cleanup注解可以自动关闭资源。

您可以使用@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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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);
}
}
}

编译后Lombok会生成如下代码。

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

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();
}
}
}
}

在finally块中,仅当给定的资源不为null时才调用cleanup方法。但是,如果在代码中使用delomboklombok.lombok.lombok分析如果静态代码分析可以确定不需要空检查,则插入(Object o)以防止出现警告。编译龙目岛.jar在类路径上删除该方法调用,因此没有运行时依赖关系。
如果代码抛出异常,然后触发的cleanup方法调用也抛出异常,那么cleanup调用抛出的异常将隐藏原始异常。你不应该依赖这个“特性”。优选地,lombok希望生成这样的代码:如果主体抛出了异常,那么close调用抛出的任何异常都将被默默地吞没(但是如果主体以任何其他方式退出,那么close调用抛出的异常将不会被吞没)。lombok的作者目前还不知道一种可行的方法来实现这个方案,但是如果java更新允许,或者我们找到了一种方法,我们就会修复它。
您仍然需要处理cleanup方法可以生成的任何异常!

@Getter/@Setter

您可以使用@Getter和/或@Setter注释任何字段,以使lombok自动生成默认的getter / setter。
默认的getter只是返回该字段,如果将该字段称为foo,则将其命名为getFoo(如果该字段的类型为boolean,则为isFoo)。如果字段名为foo,返回void且采用与该字段相同类型的1个参数,则默认的setter名为setFoo。它只是将字段设置为此值。

除非您明确指定一个AccessLevel,否则生成的getter / setter方法将是公共的,如下例所示。合法访问级别为PUBLIC,PROTECTED,PACKAGE和PRIVATE。

您还可以在类上放置@Getter和/或@Setter批注。在这种情况下,就好像您使用该注释来注释该类中的所有非静态字段一样。

您始终可以使用特殊的AccessLevel.NONE访问级别来手动禁用任何字段的getter / setter生成。这使您可以覆盖类上的@ Getter,@ Setter或@Data批注的行为。

要将注释放在生成的方法上,可以使用onMethod = @ ({@ AnnotationsHere});要将注释放在生成的setter方法的唯一参数上,可以使用onParam = @ ({@ AnnotationsHere})。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。

lombok v1.12.0中的新增功能:字段上的javadoc现在将被复制到生成的getter和setter中。通常,将复制所有文本,并将@return移动到getter,而将@param行移动到setter。移动的意思是:从字段的javadoc中删除。还可以为每个获取器/设置器定义唯一的文本。为此,您创建一个名为GETTER和/或SETTER的“部分”。一节是Javadoc中的一行,其中包含2个或多个破折号,然后是文本“ GETTER”或“ SETTER”,后接2个或多个破折号,而该行上没有其他字符。如果使用节,则该节的@return和@param剥离将不再完成(将@return或@param行移到该节中)。

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

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);
}
}

编译后Lombok会生成如下代码。

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
42
43

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;
}
}

为了生成方法名称,该字段的第一个字符(如果是小写字符)将用标题区分大小写,否则将保持不变。然后,以get / set / is为前缀。

如果已经存在具有相同名称(不区分大小写)和相同参数计数的方法,则不会生成任何方法。例如,即使已经有方法getFoo(String … x),即使已经存在该方法,也不会生成getFoo()。存在此警告以防止混淆。如果由于这个原因而跳过方法的生成,则会发出警告。 Varargs计数为0到N个参数。您可以使用@ lombok.experimental.Tolerate标记任何方法以将其从lombok中隐藏。

对于以开头的布尔型字段,紧随其后的是标题大小写字母,没有前缀可生成吸气剂名称。

布尔值的任何变化都不会导致使用is前缀而不是get前缀;例如,返回java.lang.Boolean会生成get前缀,而不是is前缀。

流行库中的许多注释表示非空,例如javax.annotation.Nonnull(如果存在于字段中)会在生成的setter中导致显式的null检查。

各种关于可空性的众所周知的注释(例如org.eclipse.jdt.annotation.NonNull)会自动复制到正确的位置(用于getter的方法,用于setter的参数)。您可以指定应始终通过lombok配置键lombok.copyableAnnotations复制的其他注释。

您可以使用@Getter或@Setter注释对类进行注释。这样做等效于用该注释注释该类中的所有非静态字段。字段上的@ Getter / @ Setter注释优先于类上的注释。

使用AccessLevel.NONE访问级别不会产生任何结果。它仅在与@Data或类范围内的@Getter或@Setter结合使用时才有用。

@Getter也可以用于枚举。 @Setter不能,不是出于技术原因,而是出于一个务实的理由:枚举的设置者是一个非常糟糕的主意。

@ToString

任何类定义都可以用@ToString注释,以使lombok生成toString()方法的实现。默认情况下,它将按顺序打印您的班级名称以及每个字段,并以逗号分隔。

通过将includeFieldNames参数设置为true,可以为toString()方法的输出增加一些清晰度(但也要有一些长度)。

默认情况下,将打印所有非静态字段。如果要跳过某些字段,可以使用@ ToString.Exclude注释这些字段。另外,您可以使用@ToString(onlyExplicitlyIncluded = true)来指定要使用的字段,然后使用@ ToString.Include标记要包括的每个字段。

通过将callSuper设置为true,可以将toString的超类实现的输出包含到输出中。请注意,java.lang.Object中toString()的默认实现几乎毫无意义,因此除非扩展了另一个类,否则您可能不希望这样做。

您还可以在toString中包含方法调用的输出。只能包含不带参数的实例(非静态)方法。这样做,用@ ToString.Include标记该方法。

您可以使用@ ToString.Include(name =“ some other name”)更改用于标识成员的名称,还可以通过@ ToString.Include(rank = -1)更改成员的打印顺序。没有等级的成员被认为是等级0,等级更高的成员首先被打印,等级相同的成员以它们在源文件中出现的顺序打印。

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

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;
}
}
}

编译后Lombok会生成如下代码。

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
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) + ")";
}
}

如果有一个名为toString的方法没有参数,则无论返回类型如何,都不会生成任何方法,而是会发出警告,说明您的@ToString注释不起作用。您可以使用@ lombok.experimental.Tolerate标记任何方法以将其从lombok中隐藏。

数组是通过Arrays.deepToString打印的,这意味着包含它们的数组将导致StackOverflowErrors。但是,这种行为与例如数组列表。

如果一个方法被标记为包含,并且与字段具有相同的名称,它将替换该字段的toString输出(包括该方法,将该字段排除在外,并且该方法的输出被打印在要打印该字段的位置)。

在lombok 1.16.22之前,可以使用@ToString批注的of和exclude参数进行包含/排除。仍然支持此旧式包含机制,但将来会不推荐使用。

在成员上同时具有@ ToString.Exclude和@ ToString.Include会生成警告。在这种情况下,该成员将被排除在外。

我们不保证在lombok版本之间使生成的toString()方法的输出保持相同。您绝对不应该设计您的API,这样无论如何,其他代码都必须被解析为toString()输出!

默认情况下,任何以$符号开头的变量都会被自动排除。您只能使用@ ToString.Include批注将其包括在内。

如果存在将要包含的字段的吸气剂,则将调用它,而不使用直接字段引用。此行为可以被抑制:
@ToString(doNotUseGetters = true)

@ToString也可用于枚举定义。

如果已通过lombok.config键lombok.addNullAnnotations配置了无效注释风味,则方法或返回类型(适用于所选风味)将使用非null注释进行注释。

@EqualsAndHashCode

任何类定义都可以使用@EqualsAndHashCode进行注释,以使lombok生成equals(Object other)和hashCode()方法的实现。默认情况下,它将使用所有非静态,非瞬态字段,但是您可以通过使用@ EqualsAndHashCode.Include标记类型成员来修改使用哪些字段(甚至指定要使用各种方法的输出)。 @ EqualsAndHashCode.Exclude。或者,可以通过使用@ EqualsAndHashCode.Include标记并使用@EqualsAndHashCode(onlyExplicitlyIncluded = true)来精确指定希望使用的字段或方法。

如果将@EqualsAndHashCode应用于扩展另一个类的类,则此功能会有些棘手。通常,为此类自动生成equals和hashCode方法是一个坏主意,因为超类还定义了字段,该字段也需要equals / hashCode代码,但不会生成此代码。通过将callSuper设置为true,可以在生成的方法中包括超类的equals和hashCode方法。对于hashCode,super.hashCode()的结果包含在哈希算法中,并且等于,如果super实现认为它与传入的对象不相等,则生成的方法将返回false。请注意,并非所有的equals实现都可以正确处理这种情况。但是,由lombok生成的equals实现确实可以正确处理这种情况,因此,如果它也具有由lombok生成的equals方法,则可以安全地调用您的超类equals。如果您有显式的超类,则必须为callSuper提供一些值,以确认您已经考虑了它。否则将导致警告。

在不扩展任何内容(扩展java.lang.Object)时将callSuper设置为true是一个编译时错误,因为它将使生成的equals()和hashCode()实现具有与简单地继承它们相同的行为。 java.lang.Object中的方法:只有相同的对象彼此相等,并且具有相同的hashCode。扩展另一个类时,未将callSuper设置为true会生成警告,因为除非超类没有(相等的重要)字段,否则lombok无法为您生成一个实现,该实现将您的超类声明的字段考虑在内。您需要编写自己的实现,或依靠callSuper链接工具。您还可以使用lombok.equalsAndHashCode.callSuper配置键。

Lombok 0.10中的新增内容:除非您的类是最终的并且扩展了java.lang.Object,否则Lombok会生成canEqual方法,这意味着JPA代理仍然可以与其基类相等,但是添加新状态的子类不会破坏equals契约。本文解释了为何需要这种方法的复杂原因:如何用Java编写平等方法。如果层次结构中的所有类都是Scala案例类和具有lombok生成的equals方法的类的混合,则所有等式都将“正常工作”。如果需要编写自己的equals方法,则在更改equals和hashCode时应始终覆盖canEqual。

Lombok 1.14.0中的新增功能:要将注释放在equals(以及相关的canEqual)方法的另一个参数上,可以使用onParam = @ __({@ AnnotationsHere})。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。

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

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;
}
}
}

编译后Lombok会生成如下代码。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

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;
}
}
}

数组是“深度”比较/ hashCoded,这意味着包含它们的数组将导致StackOverflowErrors。但是,这种行为与例如数组列表。

您可以安全地假定所使用的hashCode实现在lombok的版本之间不会改变,但是这种保证并不是一成不变的。如果使用备用哈希算法可以显着提高性能,则将在以后的版本中替代。

为了相等起见,尽管“ NaN == NaN”将返回false,但浮点数和双精度数的2个NaN(不是数字)值被视为相等。这类似于java.lang.Double的equals方法,并且实际上是确保将对象与其自身的精确副本进行比较以返回相等性所必需的。

如果有任何名为hashCode或equals的方法,无论返回类型如何,都不会生成任何方法,而是发出警告。这两种方法必须彼此同步,lombok不能保证除非生成所有方法,否则,如果其中一种或两种方法已经存在,您总是会得到警告。您可以使用@ lombok.experimental.Tolerate标记任何方法以将其从lombok中隐藏。

尝试排除不存在或无论如何都将被排除的字段(因为它们是静态的或临时的)都会在命名字段上产生警告。

如果某个方法被标记为包含,并且与字段具有相同的名称,则它将替换该字段(包含该方法,但不包括该字段)。

在lombok 1.16.22之前,可以使用@EqualsAndHashCode批注的of和exclude参数进行包含/排除。仍然支持此旧式包含机制,但将来会不推荐使用。

默认情况下,任何以$符号开头的变量都会被自动排除。您只能通过使用@ EqualsAndHashCode.Include标记它们来包括它们。

如果存在将要包含的字段的吸气剂,则将调用它,而不使用直接字段引用。此行为可以被抑制:
@EqualsAndHashCode(doNotUseGetters = true)

如果您已通过lombok.config键lombok.addNullAnnotations配置了无效注释风格,则生成的equals方法以及任何canEqual方法的参数都将使用可为空的注释进行注释。如果将@NonNullByDefault样式注释与严格的无效性检查结合使用,则这是必需的。

@XxConstructor

使用@XxConstructor注解可以自动生成构造方法,有@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor三个注解可以使用。

  • @NoArgsConstructor:生成无参构造函数。
  • @RequiredArgsConstructor:生成包含必须参数的构造函数,使用@NonNull注解的类属性为必须参数。
  • @AllArgsConstructor:生成包含所有参数的构造函数。

这组3个注释会生成一个构造函数,该构造函数将为某些字段接受1个参数,并将该参数简单地分配给该字段。

@NoArgsConstructor将生成不带参数的构造函数。如果这是不可能的(由于final字段),则将导致编译器错误,除非使用@NoArgsConstructor(force = true),否则所有final字段都将初始化为0 / false / null。对于具有约束的字段(例如@NonNull字段),不会生成检查,因此请注意,这些约束通常要等到稍后对这些字段进行适当初始化后才能实现。某些Java构造(例如,休眠和服务提供者接口)需要无参数构造函数。此注释主要与@Data或其他生成注释的构造函数之一结合使用,非常有用。

@RequiredArgsConstructor为需要特殊处理的每个字段生成一个带有1个参数的构造函数。所有未初始化的final字段都会获取一个参数,以及所有未声明其位置的未标记为@NonNull的字段。对于标有@NonNull的那些字段,还将生成显式的null检查。如果用于@NonNull标记的字段的任何参数包含null,则构造方法将引发NullPointerException。参数的顺序与字段在类中出现的顺序匹配。

@AllArgsConstructor为类中的每个字段生成一个带有1个参数的构造函数。标有@NonNull的字段将对这些参数进行空检查。

这些注释中的每一个都允许使用备用格式,其中生成的构造函数始终是私有的,并且会生成环绕私有构造函数的其他静态工厂方法。通过为注释提供staticName值来启用此模式,如下所示:@RequiredArgsConstructor(staticName =“ of”)。与常规构造函数不同,这种静态工厂方法将推断泛型。这意味着您的API用户将写入MapEntry.of(“ foo”,5),而不是更长的新MapEntry (“ foo”,5)。

要将注释放在生成的构造函数上,可以使用onConstructor = @ __({@ AnnotationsHere}),但要小心;这是一个实验功能。有关更多详细信息,请参阅onX功能的文档。

这些注释会跳过静态字段。

与大多数其他的龙目岛注释不同,显式构造函数的存在并不会阻止这些注释生成它们自己的构造函数。这意味着您可以编写自己的专用构造函数,并让lombok生成样板文件。如果发生冲突(您的一个构造函数的最终签名与lombok生成的签名相同),则会发生编译器错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}

编译后Lombok会生成如下代码。

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 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() {
}
}
}

即使字段显式初始化为null,lombok也会考虑避免满足null的要求,并且不会将字段视为“必需”参数。假设是,如果您为还标记为@NonNull的字段显式分配null,则必须知道您在做什么。

不会为没有参数的构造函数生成@ java.beans.ConstructorProperties批注。这也解释了为什么@NoArgsConstructor缺少preventConstructorProperties批注方法。生成的静态工厂方法也不会获取@ConstructorProperties,因为此批注只能添加到实际的构造函数中。

@XArgsConstructor也可以用于枚举定义。生成的构造函数将始终是私有的,因为非私有的构造函数在枚举中是不合法的。您不必指定AccessLevel.PRIVATE。

关于空值的各种众所周知的注释会导致插入空值检查,并将其复制到参数中。有关更多信息,请参见Getter / Setter文档的小字体。

由@ Data,@ Value或任何其他lombok注释生成构造函数时,不会触发flagUsage配置键。

@Data

@Data是一个方便的快捷方式批注,它将@ ToString,@ EqualsAndHashCode,@ Getter / @Setter和@RequiredArgsConstructor的功能捆绑在一起:换句话说,@ Data生成通常与简单POJO关联的所有样板(普通的旧Java对象) )和bean:所有字段的getter,所有非最终字段的setter,以及涉及类字段的适当的toString,equals和hashCode实现,以及初始化所有最终字段以及所有非最终字段的构造函数没有使用@NonNull标记的初始化程序,以确保该字段永远不会为null。

@Data就像在类上具有隐式@ Getter,@ Setter,@ ToString,@ EqualsAndHashCode和@RequiredArgsConstructor注释(除非如果已经存在任何显式编写的构造函数,则不会生成任何构造函数)。但是,这些注释的参数(例如callSuper,includeFieldNames和exclude)不能使用@Data设置。如果您需要为这些参数中的任何一个设置非默认值,只需显式添加这些注释即可; @Data很聪明,可以遵循这些注释。

所有生成的getter和setter将是公开的。要覆盖访问级别,请使用显式的@Setter和/或@Getter注释对字段或类进行注释。您还可以使用此批注(通过将其与AccessLevel.NONE结合使用)来完全禁止生成getter和/或setter。

标记为瞬态的所有字段都不会考虑使用hashCode和equals。所有静态字段都将被完全跳过(任何生成的方法都不会考虑,并且不会为它们创建setter / getter)。

如果该类已经包含一个名称和参数计数与通常会生成的任何方法相同的方法,则不会生成该方法,并且不会发出警告或错误。例如,如果您已经具有带有签名equals(AnyType param)的方法,则不会生成equals方法,即使从技术上讲,由于参数类型不同,它可能是完全不同的方法。相同的规则适用于构造函数(任何显式构造函数都会阻止@Data生成一个),以及toString,equals和所有getter和setter。您可以使用@ lombok.experimental.Tolerate标记任何构造函数或方法,以将其从lombok中隐藏。

@Data可以处理字段的泛型参数。为了减少使用泛型为类构造对象时的样板,可以使用staticConstructor参数生成私有构造函数,以及返回新实例的静态方法。这样,javac将推断变量名称。因此,通过这样声明:@Data(staticConstructor =“ of”)类Foo {private T x;},可以通过编写以下内容来创建Foo的新实例:Foo.of(5);而不用写:new Foo (5);。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}
}

编译后Lombok会生成如下代码。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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;
}
}
}

请参阅@ ToString,@ EqualsAndHashCode,@ Getter / @Setter和@RequiredArgsConstructor的小字。

关于空值的各种众所周知的注释会导致插入空值检查,并将其复制到相关位置(例如,用于getter的方法以及用于构造函数和setter的参数)。 有关更多信息,请参见Getter / Setter文档的小字体。

默认情况下,任何以$符号开头的变量都会被自动排除。 您可以通过指定显式注释(例如,@ Getter或@ToString)并使用’of’参数来包括它们。

@Value

@Value在lombok v0.11.4中作为实验功能引入。

自lombok v0.11.8起,@ Value不再暗示@Wither。

自lombok v0.12.0起,@ Value提升为主要的lombok软件包。

@Value是@Data的不变变量;默认情况下,所有字段都设为私有和最终字段,并且不会生成设置器。默认情况下,该类本身也将最终确定为final,因为不可改变性不能强加于子类。像@Data一样,还会生成有用的toString(),equals()和hashCode()方法,每个字段都有一个getter方法,并且还会生成一个覆盖每个参数的构造函数(除了在字段声明中初始化的最终字段之外) 。

实际上,@Value是以下形式的缩写:final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true,level = AccessLevel.PRIVATE)@Getter,除了明确包括任何相关方法的实现只是意味着该部分不会不会生成,也不会发出警告。例如,如果您编写自己的toString,则不会发生错误,并且lombok不会生成toString。同样,任何显式构造函数,无论参数列表如何,都意味着lombok不会生成构造函数。如果您确实希望lombok生成all-args构造函数,请将@AllArgsConstructor添加到该类中。注意,如果@Builder和@Value都在一个类上,则@Builder要使“ @Value”要使之成为公共对象的包私有allargs构造函数。您可以使用@ lombok.experimental.Tolerate标记任何构造函数或方法,以将其从lombok中隐藏。

使用字段上的显式访问级别或使用@NonFinal或@PackagePrivate批注,可以覆盖默认的最终行为和默认的私有行为。 @NonFinal也可以在类上使用,以删除最终关键字。
通过显式使用该批注,可以覆盖组成@Value的任何“部分”的任何默认行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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;
}
}

编译后Lombok会生成如下代码。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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() + ")";
}
}
}

在@Value的“部分”上查找文档:@ ToString,@ EqualsAndHashCode,@ AllArgsConstructor,@ FieldDefaults和@Getter。

对于具有泛型的类,使用静态方法作为构造函数很有用,因为通过静态方法推断泛型参数在java6中有效,并且避免了使用菱形运算符。虽然您可以通过应用显式的@AllArgsConstructor(staticConstructor =“ of”)注释来强制执行此操作,但还有@Value(staticConstructor =“ of”)注释功能,该功能会将生成的所有参数的构造函数设为私有,并生成公共static名为的方法是此私有构造方法的包装。

关于空值的各种众所周知的注释会导致插入空值检查,并将其复制到相关位置(例如,用于getter的方法以及用于构造函数和setter的参数)。有关更多信息,请参见Getter / Setter文档的小字体。

@Value是从v0.11.4到v0.11.9的实验功能(如@ lombok.experimental.Value)。此后已被移入核心程序包。旧的注释仍然存在(并且是别名)。不过,最终它将在将来的版本中删除。

无法使用@FieldDefaults来“撤消”带注释类中字段的默认默认值和最终默认值。在类中的字段上使用@NonFinal和@PackagePrivate可以覆盖此行为。

@Builder

@Builder是lombok v0.12.0中的实验功能。

@Builder获得了@Singular支持,自lombok v1.16.0起被提升为主要的lombok软件包。

自lombok v1.16.8起,带有@Singular的@Builder添加了一个清晰的方法。

@ Builder.Default功能已在lombok v1.16.16中添加。

从lombok v1.18.8开始,@ Builder(builderMethodName =“”)是合法的(并且将禁止生成builder方法)。

从lombok v1.18.8开始,@ Builder(access = AccessLevel.PACKAGE)是合法的(并将生成具有指定访问级别的构建器类,构建器方法等)。

@Builder批注为您的类生成复杂的构建器API。

@Builder使您可以自动生成使类可实例化的代码,例如:
Person.builder()。name(“ Adam Savage”)。city(“ San Francisco”)。job(“ Mythbusters”)。job(“ Unchained Reaction”)。build();

@Builder可以放在类,构造函数或方法上。虽然“在类上”和“在构造函数上”模式是最常见的用例,但使用“方法”用例最容易解释@Builder。

用@Builder注释的方法(从现在开始称为目标)将生成以下7件事:

  • 内部静态类,名为FooBuilder,具有与静态方法相同的类型参数(称为生成器)。
  • 在构建器中:目标的每个参数有一个私有的非静态非最终字段。
  • 在构建器中:包私有的无参数空构造函数。
  • 在构建器中:针对目标的每个参数的类似于“ setter”的方法:它具有与该参数相同的类型和相同的名称。如上例所示,它返回构建器本身,以便可以将setter调用链接起来。
  • 在构建器中:一个build()方法,该方法调用该方法,并在每个字段中传递。它返回与目标返回相同的类型。
  • 在构建器中:明智的toString()实现。
  • 在包含目标的类中:builder()方法,该方法创建构建器的新实例。
    如果该元素已存在,则每个列出的生成元素都将被静默跳过(忽略参数计数并仅查看名称)。这包括构建器本身:如果该类已经存在,则lombok会简单地开始在此现有类中注入字段和方法,除非要注入的字段/方法当然已经存在。但是,您不能在生成器类上放置生成lombok批注的任何其他方法(或构造函数)。例如,您不能将@EqualsAndHashCode放在构建器类上。
    @Builder可以为集合参数/字段生成所谓的“单一”方法。它们采用1个元素而不是整个列表,然后将该元素添加到列表中。例如:Person.builder()。job(“ Mythbusters”)。job(“ Unchained Reaction”)。build();将导致List 作业字段中包含2个字符串。若要获得此行为,必须使用@Singular注释字段/参数。该功能具有其自己的文档。

现在,“方法”模式已经很清楚了,将@Builder注释放在构造函数上的功能类似。实际上,构造函数只是具有特殊语法以调用它们的静态方法:它们的“返回类型”是它们构造的类,并且它们的类型参数与类本身的类型参数相同。

最后,将@Builder应用于类就像是将@AllArgsConstructor(access = AccessLevel.PACKAGE)添加到该类并将@Builder注释应用于此all-args-constructor一样。仅当您自己未编写任何显式构造函数时,此方法才有效。如果确实有显式构造函数,则将@Builder批注放在构造函数上,而不要放在类上。请注意,如果将@Value和@Builder都放在类上,则@Builder要生成“ wins”的程序包私有构造函数,而禁止@Value要生成的构造函数。

如果使用@Builder生成生成器来生成您自己的类的实例(除非将@Builder添加到不返回您自己的类型的方法中,否则通常都是这种情况),您可以使用@Builder(toBuilder = true)来生成类中的一个实例方法,称为toBuilder();它创建一个新的构建器,该构建器以该实例的所有值开始。您可以在参数(对于构造函数或方法的情况下)或字段(对于类型的@Builder情况)上放置@ Builder.ObtainVia批注,以指示从中获取该字段/参数值的替代方法实例。例如,您可以指定要调用的方法:@ Builder.ObtainVia(method =“ calculateFoo”)。

builder类的名称为FoobarBuilder,其中Foobar是目标的返回类型的简化的,用标题区分大小写的形式-即,构造函数和类型上@Builder的类型名称以及返回类型的名称对于@Builder方法。例如,如果将@Builder应用于名为com.yoyodyne.FancyList 的类,则构建器名称将为FancyListBuilder <T>。如果将@Builder应用于返回void的方法,则该构建器将命名为VoidBuilder。

构建器的可配置方面包括:

  • 构建器的类名(默认值:返回类型+’Builder’)
  • build()方法的名称(默认值:“ build”)
  • builder()方法的名称(默认值:“ builder”)
  • 如果要toBuilder()(默认值:否)
  • 所有生成的元素的访问级别(默认值:公共)。
  • (不推荐使用)如果您希望构建器的“ set”方法具有前缀,即Person.builder()。setName(“ Jane”)。build()而不是Person.builder()。name(“ Jane”)。其应为build ()。

用法示例,其中所有选项均从其默认值更改:

1
@Builder(builderClassName =“ HelloWorldBuilder”,buildMethodName =“ execute”,builderMethodName =“ helloWorld”,toBuilder = true,访问权限= AccessLevel.PRIVATE,setterPrefix =“ set”)

@Builder.Default

如果在构建会话期间从未设置过某个字段/参数,则该字段/参数始终为0 / null / false。 如果将@Builder放在类(而不是方法或构造函数)上,则可以直接在字段上指定默认值,并使用@ Builder.Default对该字段进行注释:

1
@Builder.Default private final long created = System.currentTimeMillis();

@Singular

通过使用@Singular注释对参数之一(如果使用@Builder注释方法或构造函数)或字段(如果使用@Builder注释类),lombok会将该生成器节点视为一个集合,并生成2’adder 方法而不是“设置”方法。 一个将单个元素添加到集合中,另一个将另一个集合的所有元素添加到集合中。 将不会生成仅设置集合(替换已添加的任何内容)的设置器。 还生成“清除”方法。 这些“单一”生成器非常复杂,以保证以下属性:

  • 调用build()时,产生的集合将是不可变的。
  • 调用build()后,调用“ adder”方法或“ clear”方法之一不会修改任何已生成的对象,并且,如果- - 以后再次调用build(),则将创建自创建以来添加了所有元素的另一个集合 生成器的生成。
    产生的集合将压缩为最小的可行格式,同时保持效率。

如果您的标识符是用普通英语书写的,则lombok假定上面带有@Singular的任何集合的名称都是英语的复数形式,并将尝试自动将该名称单数化。如果可能的话,附加方法将使用此名称。例如,如果您的集合称为状态,那么添加方法将自动称为状态。您还可以通过将奇异形式作为参数传递给注释,来明确指定标识符的奇异形式,例如:@Singular(“ axis”)List axes;。
如果lombok无法单数化您的标识符,或者模棱两可,则lombok会生成错误并迫使您明确指定单数名称。

下面的代码片段没有显示lombok为@Singular字段/参数生成的内容,因为它相当复杂。您可以在此处查看代码段。

如果还使用setterPrefix =“ with”,则生成的名称例如为withName(添加1个名称),withNames(添加许多名称)和clearNames(重置所有名称)。

通常,生成的“复数形式”方法(接收一个集合,并添加此集合中的每个元素)将检查是否通过与@NonNull相同的方式传递了null(默认情况下,抛出NullPointerException和适当的消息)。但是,您也可以告诉lombok忽略此类集合(因此,不添加任何内容,立即返回):@Singular(ignoreNullCollections = true)。

With Jackson

您可以自定义构建器的各个部分,例如,向构建器类添加其他方法,或者在构建器类中注释方法,方法是自己构建构建器类。 Lombok将生成您不手动添加的所有内容,并将其放入此构建器类。 例如,如果您尝试将杰克逊配置为对集合使用特定的子类型,则可以编写如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Value @Builder
@JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class)
public class JacksonExample {
@Singular(nullBehavior = NullCollectionBehavior.IGNORE) private List<Foo> foos;

@JsonPOJOBuilder(withPrefix = "")
public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta {
}

private interface JacksonExampleBuilderMeta {
@JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos)
}
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
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;
}

编译后Lombok会生成如下代码。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

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 + ")";
}
}
}

@java对java.util.NavigableMap / Set的单独支持仅在使用JDK1.8或更高版本进行编译时有效。

您不能手动提供@Singular节点的部分或全部;为此,lombok生成的代码过于复杂。如果要手动控制与某些字段或参数关联的构建器代码(的一部分),请不要使用@Singular并手动添加所需的所有内容。

排序的集合(java.util:SortedSet,NavigableSet,SortedMap,NavigableMap和guava:ImmutableSortedSet,ImmutableSortedMap)要求集合的type参数具有自然顺序(实现java.util.Comparable)。无法通过显式比较器在构建器中使用。

ArrayList用于将添加的元素存储为@Singular标记字段的调用方法,即使目标集合来自java.util包,即使该集合是集合或映射也是如此。因为lombok确保压缩生成的集合,所以无论如何都必须构造集合或映射的新后备实例,并且在构建过程中将数据存储为ArrayList比将其存储为映射或集合更有效。此行为在外部不可见,这是@Singular @Builder的java.util配方的当前实现的实现细节。

通过将toBuilder = true应用于方法,带注释的方法本身的任何类型参数也必须显示在返回类型中。

@ Builder.Default字段上的初始值设定项已删除并存储在静态方法中,以确保如果在构建中指定了值,则根本不会执行此初始值设定项。这确实意味着初始化程序无法引用此,super或任何非静态成员。如果lombok为您生成了一个构造函数,它还将使用初始化程序初始化此字段。

构建器中生成的表示具有@ Builder.Default设置的字段的字段称为propertyName $ value;。还会生成一个名为propertyName $ set的附加布尔字段,以跟踪是否已设置它。这是一个实现细节。不要编写与这些字段进行交互的代码。相反,如果要在构建器内部的自定义方法中设置属性,请调用生成的builder-setter方法。

关于空值的各种众所周知的注释会导致插入空值检查,并将其复制到构建器的“ setter”方法的参数中。有关更多信息,请参见Getter / Setter文档的小字体。

您可以通过使用@Builder(builderMethodName =“”)来抑制builder()方法的生成,例如,因为您只想要toBuilder()功能。执行此操作时,任何有关缺少@ Builder.Default批注的警告都将消失,因为仅使用toBuilder()生成构建器实例时,此类警告不相关。

您可以将@Builder用于复制构造函数:foo.toBuilder()。build()进行浅表克隆。如果只想要此功能,请考虑通过以下方式抑制生成器方法的生成:@Builder(toBuilder = true,builderMethodName =“”)。

由于javac处理静态导入的一种特殊方式,尝试对static builder()方法进行非星形静态导入是行不通的。使用星型静态导入:import static TypeThatHasABuilder。*;或不静态导入builder方法。

如果将访问级别设置为PROTECTED,则在构建器类内部生成的所有方法实际上都将作为公共生成。内部类中protected关键字的含义是不同的,并且PROTECTED会指示的确切行为(允许访问同一包中的任何源,以及外部类中的任何子类都不能用@Builder标记) ,并且将内部成员标记为公开就可以了。

如果您已通过lombok.config键lombok.addNullAnnotations配置了无效注解样式,则任何为@Singular标记的属性生成的复数形式生成的构建器方法(这些复数形式方法采用某种形式的集合并添加所有元素)将在参数。您通常会得到一个非空值,但是如果您已配置了将null行为作为集合传递给IGNORE的行为,则会生成一个可为空的注释。

@SneakyThrows

@SneakyThrows可用于偷偷地抛出已检查的异常,而无需在方法的throws子句中实际声明。当然,应该谨慎使用这种有争议的功能。 lombok生成的代码不会忽略,包装,替换或以其他方式修改引发的检查异常;它只是伪造编译器。在JVM(类文件)级别上,无论方法的throws子句如何,都可以引发所有已检查或未检查的异常,这就是为什么这样做的原因。

当您想退出选中的异常机制时,常见的用例围绕两种情况:

  • 不必要严格的接口,例如Runnable -无论异常是否从run()方法中传播出去,无论是否经过检查,它都将传递给线程的未处理异常处理程序。捕获已检查的异常并将其包装在某种RuntimeException中只会掩盖问题的真正原因。
  • “不可能”例外。例如,新的String(someByteArray,“ UTF-8”);声明它可以引发UnsupportedEncodingException,但根据JVM规范,UTF-8必须始终可用。当您使用String对象时,此处的UnsupportedEncodingException几乎与ClassNotFoundError差不多,而您也不会抓住它们!

当使用lambda语法(arg-> action)时,受到不必要的严格接口的约束尤其常见。但是,lambda不能被注释,这意味着将@SneakyThrows与lambda结合使用并不是那么容易。

请注意,不可能直接捕获偷偷地抛出的检查类型,因为javac不允许您为try主体中没有方法调用声明为抛出的异常类型编写catch块。在上面列出的两个用例中,此问题均无关紧要,因此请将此作为警告,如果您不进行任何深思熟虑就不要使用@SneakyThrows机制!

您可以将任意数量的异常传递给@SneakyThrows批注。如果没有通过任何异常,则可以偷偷地抛出任何异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

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();
}
}

编译后Lombok会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}
}
}

因为@SneakyThrows是实现细节,而不是方法签名的一部分,所以如果您在不调用任何引发此异常的方法时尝试将已声明的异常声明为偷偷摸摸地抛出,则会出现错误。 (这样做对于引发语句以容纳子类是完全合法的)。同样,@ SneakyThrows不继承。

对于那些反对者:开箱即用,Eclipse将为未捕获的异常提供一个“快速修复”,该异常将有问题的语句包装在try / catch块中,而catch块中仅包含e.printStackTrace()。与仅仅偷偷地抛出异常相比,这是如此无用,以至于Roel和Reinier认为被检查的异常系统还不够完善,因此感到有充分的道理,因此可以选择退出机制。

如果将@SneakyThrows放在构造函数上,则对@SneakyThrows的处理不会包括对同级或超级构造函数的任何调用。这是我们无法解决的Java限制:调用同级/超级构造函数必须是构造函数中的第一条语句;它们不能放置在try / catch块中。

@SneakyThrows在空方法上,或者在构造函数为空或仅具有对同级/超级构造函数的调用时,不会导致try / catch块和警告。

@Synchronized

@Synchronized是同步方法修饰符的更安全的变体。与同步一样,注释只能在静态和实例方法上使用。它的操作类似于synced关键字,但是它锁定在不同的对象上。关键字对此进行了锁定,但注解锁定了一个名为$ lock的私有字段。
如果该字段不存在,则会为您创建。如果注释静态方法,则注释将锁定在名为$ LOCK的静态字段上。

如果需要,可以自己创建这些锁。如果您已经自己创建了$ lock和$ LOCK字段,则当然不会生成它们。您也可以通过将其指定为@Synchronized批注的参数来选择锁定另一个字段。在此用法变型中,不会自动创建字段,并且您必须自己明确创建它们,否则会发出错误。

锁定此对象或您自己的类对象可能会产生不利的副作用,因为不受您控制的其他代码也可能会锁定这些对象,这可能会导致争用条件和其他与线程相关的令人讨厌的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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");
}
}

编译后Lombok会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

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");
}
}
}

如果$ lock和/或$ LOCK是自动生成的,则字段将用一个空的Object []数组初始化,而不仅仅是一个新的Object()初始化,因为大多数代码片段都在实际使用中显示了此模式。 Lombok这样做是因为新对象不可序列化,而0大小的数组则可序列化。因此,使用@Synchronized不会阻止对象被序列化。

类中至少有一个@Synchronized方法意味着将有一个锁字段,但是如果以后删除所有这些方法,将不再有锁字段。这意味着您预定的serialVersionUID会更改。如果您打算通过Java的序列化机制长期存储它们,我们建议您始终向类中添加serialVersionUID。如果这样做,从方法中删除所有@Synchronized批注不会破坏序列化。

如果您想知道为什么当您为锁对象选择自己的名称时为什么不自动生成字段:因为否则在字段名称中输入错误会导致很难发现错误!

@With

@Wither是lombok v0.11.4中的实验功能。

@Wither重命名为@With,并从lombok v1.18.10中移出了实验版,并移入了核心程序包。

不变属性的二传手的第二个最佳选择是构造对象的克隆,但为此字段添加一个新值。生成此克隆的方法正是@With生成的方法:withFieldName(newValue)方法,该方法生成一个克隆,但关联字段的新值除外。

例如,如果您创建公共类Point {private final int x,y; },setter没有意义,因为这些字段是最终字段。 @With可以为您生成一个withX(int newXValue)方法,该方法将返回一个新点,其中x的值与y的值相同。

@With依赖于所有字段的构造函数才能完成其工作。如果此构造函数不存在,则@With批注将导致编译时错误消息。您可以使用Lombok自己的@AllArgsConstructor,或者因为Value也将自动生成所有args构造函数,因此您也可以使用它。如果您手动编写此构造函数,当然也可以接受。它必须包含相同词法顺序的所有非静态字段。

与@Setter一样,您可以指定访问级别,以防生成的with方法不是public:
@With(level = AccessLevel.PROTECTED)。与@Setter一样,您也可以在类型上放置@With注释,这意味着将为每个字段(甚至非最终字段)生成with方法。

要将注释放在生成的方法上,可以使用onMethod = @ __({@ AnnotationsHere})。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。

字段上的javadoc将被复制为使用方法生成。通常,将复制所有文本,并将@param移至with方法,而@return行将从with方法的javadoc中删除。移动的意思是:从字段的javadoc中删除。也可以为with方法的javadoc定义唯一的文本。为此,您创建一个名为WITH的“部分”。一节是Javadoc中的一行,其中包含2个或更多的破折号,然后是文本“ WITH”,后跟2个或更多的破折号,而该行上没有其他内容。如果您使用节,则将不再执行该节的@return和@param剥离/复制(将@param行移至该节中)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

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;
}
}

编译后Lombok会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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);
}
}

无法为静态字段生成With方法,因为这没有任何意义。

可以为抽象类生成With方法,但这会生成具有适当签名的抽象方法。

将@With应用于类型时,将跳过静态字段和名称以$开头的字段。

为了生成方法名称,该字段的第一个字符(如果是小写字符)将用标题区分大小写,否则将保持不变。然后,以开头。

如果已经存在具有相同名称(不区分大小写)和相同参数计数的方法,则不会生成任何方法。例如,即使已经有一个方法withX(String … x),即使在技术上可以实现该方法,也不会生成withX(int x)。存在此警告以防止混淆。如果由于这个原因而跳过方法的生成,则会发出警告。 Varargs计数为0到N个参数。

关于空值的各种众所周知的注释会导致插入空值检查,并将其复制到参数中。有关更多信息,请参见Getter / Setter文档的小字体。

如果已通过lombok.config键lombok.addNullAnnotations配置了无效注释风味,则方法或返回类型(适用于所选风味)将使用非null注释进行注释。

@Getter(lazy=true)

@Getter(lazy=true) was introduced in Lombok v0.10.

您可以让lombok生成一个将在第一次调用此getter时计算一次值的getter,然后将其缓存。 如果计算该值占用大量CPU或该值占用大量内存,这将很有用。 要使用此功能,请创建一个私有的最终变量,使用运行起来很昂贵的表达式对其进行初始化,然后使用@Getter(lazy = true)注释您的字段。 首次调用getter时,该字段将对您代码的其余部分隐藏,并且该表达式将不超过一次被求值。 没有魔术标记值(即即使昂贵的计算结果为空,结果也会被缓存),并且昂贵的计算不必是线程安全的,因为lombok负责锁定。

如果初始化表达式很复杂或包含泛型,建议将代码移到私有(如果可能的话)方法,然后调用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
}

编译后Lombok会生成如下代码。

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

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;
}
}

@Log

在lombok v0.10中添加了各种@Log变体。 lombok 0.10中的新增功能:您可以使用日志注释注释任何类,以使lombok生成记录器字段。
记录器的名称为log,字段的类型取决于您选择的记录器。

lombok v1.16.24中的新增功能:添加了Google的FluentLogger(通过@Flogger)。

lombok v1.18.10中的新增功能:@CustomLog的添加,您可以通过配置如何使用配置键创建记录器来添加任何记录器。

您将@Log的变体放在您的类上(适用于您所使用的日志记录系统的任何一种); 然后,您将拥有一个静态的最终日志字段,该字段将按照所使用的日志记录框架通常规定的方式进行初始化,然后您可以使用该方法编写日志语句。

有几种选择:

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
@CommonsLog
Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@Flogger
Creates private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();

@JBossLog
Creates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);

@Log
Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

@Log4j
Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2
Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

@Slf4j
Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

@XSlf4j
Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

@CustomLog
Creates private static final com.foo.your.Logger log = com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
This option requires that you add a configuration to your lombok.config file to specify what @CustomLog should do.

例如:lombok.log.custom.declaration = com.foo.your.Logger com.foo.your.LoggerFactory.createYourLog(TYPE)(TOPIC)会产生以上声明。首先是一个类型,它是记录器的类型,然后是一个空格,然后是记录器工厂的类型,然后是一个点,然后是记录器工厂方法的名称,然后是1或2个参数定义;最多有一个TOPIC定义,最多一个没有TOPIC的定义。每个参数定义都指定为带括号的逗号分隔的参数种类列表。选项包括:TYPE(作为类传递此@Log装饰类型),NAME(传递此@Log装饰类型的完全限定名称),TOPIC(传递通过@CustomLog注释设置的显式选择的主题字符串)和NULL(传递null)。

记录器类型是可选的;如果省略,则使用记录器出厂类型。 (因此,如果记录器类具有创建记录器的静态方法,则可以缩短记录器定义)。

如果有一个公共的,开源的,有些常用的日志记录框架,但我们没有明确的注释,请与我们联系。 @CustomLog的主要目的是支持您内部的私有日志记录框架。

默认情况下,记录器的主题(或名称)将是带有@Log注释的类的(名称)。可以通过指定topic参数来自定义。例如:@ XSlf4j(topic =“ reporting”)。

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
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");
}
}

编译后Lombok会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

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");
}
}

二 实验特征

实验功能在常规的lombok安装中可用,但不像lombok的主要功能那样得到强大的支持。 特别是实验功能:

  • 没有经过测试以及核心功能。
  • 不要像核心功能一样快地修复错误。
  • 可能会有可能会发生变化的API,如果我们找到不同的更好的方法来解决相同的问题,则可能会大为改变。
  • 如果该功能难以支持或没有足够的模板,可能会完全消失。

能够收到社区积极反馈并似乎产生清晰,灵活的代码的功能最终将被接受为核心功能,并退出实验套件。

1
2
3
4
5
6
7
8
9
10
11
/**
* Created by macro on 2020/12/17.
*/
@Slf4j
public class LogSlf4jExample {
public static void main(String[] args) {
log.info("level:{}","info");
log.warn("level:{}","warn");
log.error("level:{}", "error");
}
}

编译后Lombok会生成如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
public class LogSlf4jExample {
private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class);

public LogSlf4jExample() {
}

public static void main(String[] args) {
log.info("level:{}", "info");
log.warn("level:{}", "warn");
log.error("level:{}", "error");
}
}

var

可修改的局部变量,其类型通过分配值来推断。

@Accessors

面向getter和setter的更流畅的API。

@ExtensionMethod

Annoying API? Fix it yourself: Add new methods to existing types!

@FieldDefaults

New default field modifiers for the 21st century.

@Delegate

Don’t lose your composition.

onMethod= / onConstructor= / onParam=

Sup dawg, we heard you like annotations, so we put annotations in your annotations so you can annotate while you’re annotating.

@UtilityClass

Utility, metility, wetility! Utility classes for the masses.

@Helper

With a little help from my friends… Helper methods for java.

@FieldNameConstants

Name… that… field! String constants for your field’s names.

@SuperBuilder

Bob now knows his ancestors: Builders with fields from superclasses, too.

@Tolerate

Skip, jump, and forget! Make lombok disregard an existing method or constructor.

@Jacksonized

Bob, meet Jackson. Lets make sure you become fast friends.