文章目錄
- 一、準備
- 二、原理
- 三、使用
- 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。
-
被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");
}
}