效率提高 10 倍!一份不可多得的 Lombok 學習指南

效率提高 10 倍!一份不可多得的 Lombok 學習指南

作者 | semlinker

鏈接 | segmentfault.com/a/1190000020864572

一、Lombok 簡介

Lombok 是一款 Java 開發插件,使得 Java 開發者可以通過其定義的一些註解來消除業務工程中冗長和繁瑣的代碼,尤其對於簡單的 Java 模型對象(POJO)。在開發環境中使用 Lombok 插件後,Java 開發人員可以節省出重複構建,諸如 hashCode 和 equals 這樣的方法以及各種業務對象模型的 accessor 和 toString 等方法的大量時間。對於這些方法,Lombok 能夠在編譯源代碼期間自動幫我們生成這些方法,但並不會像反射那樣降低程序的性能。

二、Lombok 安裝

2.1 構建工具

Gradle

在 <code>build.gradle/<code>文件中添加 lombok 依賴:

<code>dependencies {
compileOnly 'org.projectlombok:lombok:1.18.10'

annotationProcessor 'org.projectlombok:lombok:1.18.10'
}/<code>

Maven

在 Maven 項目的 <code>pom.xml/<code>文件中添加 lombok 依賴:

<code><dependency>
<groupid>org.projectlombok/<groupid>
<artifactid>lombok/<artifactid>
<version>1.18.10/<version>
<scope>provided/<scope>
/<dependency>/<code>

Ant

假設在 <code>lib/<code>目錄中已經存在<code>lombok.jar/<code>,然後設置 javac 任務:

<code><javac>
<classpath>
<strong>/<javac>/<code>

2.2 IDE

由於 Lombok 僅在編譯階段生成代碼,所以使用 Lombok 註解的源代碼,在 IDE 中會被高亮顯示錯誤,針對這個問題可以通過安裝 IDE 對應的插件來解決。這裡不詳細展開,具體的安裝方式可以參考 Setting up Lombok with Eclipse and IntelliJ 這篇文章。

三、Lombok 詳解

注意:以下示例所使用的 Lombok 版本是 1.18.10

3.1 @Getter and @Setter 註解

你可以使用 <code>@Getter/<code>或<code>@Setter/<code>註釋任何類或字段,Lombok 會自動生成默認的 getter/setter 方法。

@Getter 註解

<code>@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {

lombok.AccessLevel value default lombok.AccessLevel.PUBLIC;
AnyAnnotation onMethod default {};

boolean lazy default false;
}/<code>

@Setter 註解

<code>@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {

lombok.AccessLevel value default lombok.AccessLevel.PUBLIC;
AnyAnnotation onMethod default {};
AnyAnnotation onParam default {};
}/<code>

使用示例

<code>package com.semlinker.lombok;

@Getter
@Setter

public class GetterAndSetterDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class GetterAndSetterDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;

publicGetterAndSetterDemo {
}


public String getFirstName {
return this.firstName;
}

public voidsetFirstName(String firstName) {
this.firstName = firstName;
}
}/<code>

Lazy Getter

<code>@Getter/<code>註解支持一個 lazy 屬性,該屬性默認為 false。當設置為 true 時,會啟用延遲初始化,即當首次調用 getter 方法時才進行初始化。

Tips:關注微信公眾號:Java後端,每日獲取博文的推送。

示例

<code>package com.semlinker.lombok;

public class LazyGetterDemo {
public static voidmain(String[] args) {
LazyGetterDemo m = new LazyGetterDemo;
System.out.println("Main instance is created");
m.getLazy;
}

@Getter
private final String notLazy = createValue("not lazy");

@Getter(lazy = true)
private final String lazy = createValue("lazy");

private String createValue(String name) {
System.out.println("createValue(" + name + ")");
return ;
}
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class LazyGetterDemo {
private final String notLazy = this.createValue("not lazy");
private final AtomicReference<object> lazy = new AtomicReference;


public String getNotLazy {
return this.notLazy;
}

public String getLazy {
Object value = this.lazy.get;
if (value == ) {
synchronized(this.lazy) {
value = this.lazy.get;
if (value == ) {
String actualValue = this.createValue("lazy");

value = actualValue == ? this.lazy : actualValue;
this.lazy.set(value);
}
}
}

return (String)((String)(value == this.lazy ? : value));
}
}/<object>/<code>

通過以上代碼可知,調用 getLazy 方法時,若發現 value 為 ,則會在同步代碼塊中執行初始化操作。

3.2 Constructor Annotations

@NoArgsConstructor 註解

使用 <code>@NoArgsConstructor/<code>註解可以為指定類,生成默認的構造函數,<code>@NoArgsConstructor/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {

String staticName default "";
AnyAnnotation onConstructor default {};

AccessLevel access default lombok.AccessLevel.PUBLIC;

booleanforce default false;
}/<code>

示例

<code>package com.semlinker.lombok;

@NoArgsConstructor(staticName = "getInstance")
public class NoArgsConstructorDemo {

private long id;
private String name;
private int age;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class NoArgsConstructorDemo {
private long id;
private String name;
private int age;

privateNoArgsConstructorDemo {
}

public static NoArgsConstructorDemo getInstance {
return new NoArgsConstructorDemo;
}
}/<code>

@AllArgsConstructor 註解

使用 <code>@AllArgsConstructor/<code>註解可以為指定類,生成包含所有成員的構造函數,<code>@AllArgsConstructor/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AllArgsConstructor {

String staticName default "";
AnyAnnotation onConstructor default {};

AccessLevel access default lombok.AccessLevel.PUBLIC;
}/<code>

示例

<code>package com.semlinker.lombok; 


@AllArgsConstructor
public class AllArgsConstructorDemo {
private long id;
private String name;
private int age;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class AllArgsConstructorDemo {
private long id;
private String name;
private int age;

publicAllArgsConstructorDemo(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}/<code>

@RequiredArgsConstructorDemo 註解

使用 <code>@RequiredArgsConstructor/<code>註解可以為指定類必需初始化的成員變量,如 final 成員變量,生成對應的構造函數,<code>@RequiredArgsConstructor/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface RequiredArgsConstructor {

String staticName default "";
AnyAnnotation onConstructor default {};

AccessLevel access default lombok.AccessLevel.PUBLIC;
}/<code>

示例

<code>package com.semlinker.lombok;

@RequiredArgsConstructor
public class RequiredArgsConstructorDemo {
private final long id;
private String name;
private int age;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class RequiredArgsConstructorDemo {
private final long id;
private String name;
private int age;

publicRequiredArgsConstructorDemo(long id) {
this.id = id;
}
}/<code>

3.3 @EqualsAndHashCode 註解

使用 <code>@EqualsAndHashCode/<code>註解可以為指定類生成 equals 和 hashCode 方法,<code>@EqualsAndHashCode/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {

String exclude default {};

String of default {};

boolean callSuper default false;

boolean doNotUseGetters default false;

AnyAnnotation onParam default {};

@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
String replaces default "";
}
}/<code>

示例

<code>package com.semlinker.lombok;

@EqualsAndHashCode
public class EqualsAndHashCodeDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class EqualsAndHashCodeDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;

public EqualsAndHashCodeDemo {
}

public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof EqualsAndHashCodeDemo)) {
return false;
} else {

EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o;
if (!other.canEqual(this)) {
return false;
} else {

}
}

public int hashCode {
int PRIME = true;
int result = 1;
Object $firstName = this.firstName;
int result = result * 59 + ($firstName == ? 43 : $firstName.hashCode);
Object $lastName = this.lastName;
result = result * 59 + ($lastName == ? 43 : $lastName.hashCode);
Object $dateOfBirth = this.dateOfBirth;
result = result * 59 + ($dateOfBirth == ? 43 : $dateOfBirth.hashCode);
return result;
}
}/<code>

3.4 @ToString 註解

使用 <code>@ToString/<code>註解可以為指定類生成 toString 方法,<code>@ToString/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {

booleanincludeFieldNames default true;

String exclude default {};

String of default {};


booleancallSuper default false;

booleandoNotUseGetters default false;


booleanonlyExplicitlyIncluded default false;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
int rank default 0;
String name default "";
}
}/<code>

示例

<code>package com.semlinker.lombok;

@ToString(exclude = {"dateOfBirth"})
public class ToStringDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class ToStringDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;

publicToStringDemo {
}

public String toString {
return "ToStringDemo(firstName=" + this.firstName + ", lastName=" +
this.lastName + ")";
}
}/<code>

3.5 @Data 註解

<code>@Data/<code>註解與同時使用以下的註解的效果是一樣的:

  • @ToString

  • @Getter

  • @Setter

  • @RequiredArgsConstructor

  • @EqualsAndHashCode

<code>@Data/<code>註解的定義如下:

<code>@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
String staticConstructor default "";
}/<code>

示例

<code>package com.semlinker.lombok;

@Data
public class DataDemo {
private Long id;
private String summary;
private String description;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class DataDemo {
private Long id;
private String summary;
private String description;

publicDataDemo {
}


public Long getId {
return this.id;
}

public voidsetId(Long id) {
this.id = id;
}

public booleanequals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof DataDemo)) {
return false;
} else {
DataDemo other = (DataDemo)o;
if (!other.canEqual(this)) {

return false;
} else {

}
}
}

protected booleancanEqual(Object other) {
return other instanceof DataDemo;
}

public inthashCode {
int PRIME = true;
int result = 1;
Object $id = this.getId;
int result = result * 59 + ($id == ? 43 : $id.hashCode);
Object $summary = this.getSummary;
result = result * 59 + ($summary == ? 43 : $summary.hashCode);
Object $description = this.getDescription;
result = result * 59 + ($description == ? 43 : $description.hashCode);
return result;
}

public String toString {
return "DataDemo(id=" + this.getId + ", summary=" + this.getSummary + ", description=" + this.getDescription + ")";
}
}/<code>

3.6 @Log 註解

若你將 <code>@Log/<code>的變體放在類上(適用於你所使用的日誌記錄系統的任何一種);之後,你將擁有一個靜態的 final log 字段,然後你就可以使用該字段來輸出日誌。

<code>@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);

@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);/<code>

3.7 @Synchronized 註解

<code>@Synchronized/<code>是同步方法修飾符的更安全的變體。與<code>synchronized/<code>一樣,該註解只能應用在靜態和實例方法上。它的操作類似於<code>synchronized/<code>關鍵字,但是它鎖定在不同的對象上。<code>synchronized/<code>關鍵字應用在實例方法時,鎖定的是 this 對象,而應用在靜態方法上鎖定的是類對象。對於 @Synchronized 註解聲明的方法來說,它鎖定的是<code>$LOCK/<code>或<code>$lock/<code>。<code>@Synchronized/<code>註解的定義如下:

<code>@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Synchronized {

String value default "";
}/<code>

示例

<code>package com.semlinker.lombok;

public class SynchronizedDemo {
private final Object readLock = new Object;

@Synchronized

public static voidhello {
System.out.println("world");
}

@Synchronized
public intanswerToLife {
return 42;
}

@Synchronized("readLock")
public voidfoo {
System.out.println("bar");
}
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class SynchronizedDemo {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object;

publicSynchronizedDemo {
}

public static voidhello {
synchronized($LOCK) {
System.out.println("world");
}
}

public intanswerToLife {
synchronized(this.$lock) {
return 42;

}
}

public voidfoo {
synchronized(this.readLock) {
System.out.println("bar");
}
}
}/<code>

3.8 @Builder 註解

使用 <code>@Builder/<code>註解可以為指定類實現建造者模式,該註解可以放在類、構造函數或方法上。<code>@Builder/<code>註解的定義如下:

<code>@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}


String builderMethodName default "builder";

String buildMethodName default "build";

String builderClassName default "";

boolean toBuilder default false;

AccessLevel access default lombok.AccessLevel.PUBLIC;

@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
String field default "";
String method default "";
boolean isStatic default false;
}
}/<code>

示例

<code>package com.semlinker.lombok;

@Builder
public class BuilderDemo {
private final String firstname;
private final String lastname;
private final String email;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class BuilderDemo {
private final String firstname;
private final String lastname;
private final String email;

BuilderDemo(String firstname, String lastname, String email) {
this.firstname = firstname;
this.lastname = lastname;
this.email = email;
}

public static BuilderDemo.BuilderDemoBuilder builder {
return new BuilderDemo.BuilderDemoBuilder;
}

public static class BuilderDemoBuilder {
private String firstname;
private String lastname;
private String email;

BuilderDemoBuilder {
}

public BuilderDemo.BuilderDemoBuilder firstname(String firstname) {
this.firstname = firstname;
return this;
}

public BuilderDemo.BuilderDemoBuilder lastname(String lastname) {
this.lastname = lastname;
return this;
}


public BuilderDemo.BuilderDemoBuilder email(String email) {
this.email = email;
return this;
}

public BuilderDemo build {
return new BuilderDemo(this.firstname, this.lastname, this.email);
}

public String toString {
return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")";
}
}
}/<code>

3.9 @SneakyThrows 註解

<code>@SneakyThrows/<code>註解用於自動拋出已檢查的異常,而無需在方法中使用 throw 語句顯式拋出。<code>@SneakyThrows/<code>註解的定義如下:

<code>@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {

Class extends Throwable> value default java.lang.Throwable.class;
}/<code>

示例

<code>package com.semlinker.lombok;

public class SneakyThrowsDemo {
@SneakyThrows
@Override
protected Object clone {
return super.clone;
}
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class SneakyThrowsDemo {
publicSneakyThrowsDemo {
}

protected Object clone {
try {
return super.clone;
} catch (Throwable var2) {
throw var2;
}
}
}/<code>

3.10 @Non 註解

你可以在方法或構造函數的參數上使用 <code>@Non/<code>註解,它將會為你自動生成非空校驗語句。<code>@Non/<code>註解的定義如下:

<code>@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface Non {
}/<code>

示例

<code>package com.semlinker.lombok;

public class NonDemo {
@Getter
@Setter
@Non
private String name;
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class NonDemo {
@Non
private String name;

publicNonDemo {
}

@Non
public String getName {
return this.name;
}

public voidsetName(@Non String name) {
if (name == ) {
throw new PointerException("name is marked non- but is ");
} else {
this.name = name;
}
}
}/<code>

3.11 @Clean 註解

<code>@Clean/<code>註解用於自動管理資源,用在局部變量之前,在當前變量範圍內即將執行完畢退出之前會自動清理資源,自動生成<code>try-finally/<code>這樣的代碼來關閉流。

<code>@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {

String value default "close";
}/<code>

示例

<code>package com.semlinker.lombok; 


public class CleanupDemo {
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);
}
}
}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>package com.semlinker.lombok;

public class CleanupDemo {
public CleanupDemo {
}

public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream(args[0]);

try {
FileOutputStream out = new FileOutputStream(args[1]);

try {
byte b = new byte[10000];

while(true) {
int r = in.read(b);
if (r == -1) {
return;
}

out.write(b, 0, r);
}
} finally {
if (Collections.singletonList(out).get(0) != ) {
out.close;
}

}
} finally {
if (Collections.singletonList(in).get(0) != ) {
in.close;
}

}
}
}/<code>

3.11 @With 註解

在類的字段上應用 <code>@With/<code>註解之後,將會自動生成一個<code>withFieldName(newValue)/<code>的方法,該方法會基於 newValue 調用相應構造函數,創建一個當前類對應的實例。<code>@With/<code>註解的定義如下:

<code>@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface With {
AccessLevel value default AccessLevel.PUBLIC;

With.AnyAnnotation onMethod default {};

With.AnyAnnotation onParam default {};

@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
public @interface AnyAnnotation {
}
}/<code>

示例

<code>public class WithDemo {
@With(AccessLevel.PROTECTED)
@Non
private final String name;
@With
private final int age;

publicWithDemo(String name, int age) {
if (name == ) throw new PointerException;
this.name = name;
this.age = age;
}

}/<code>

以上代碼經過 Lombok 編譯後,會生成如下代碼:

<code>public class WithDemo {
@Non
private final String name;
private final int age;

publicWithDemo(String name, int age) {
if (name == ) {
throw new PointerException;
} else {
this.name = name;
this.age = age;
}
}

protected WithDemo withName(@Non String name) {
if (name == ) {
throw new PointerException("name is marked non- but is ");
} else {
return this.name == name ? this : new WithDemo(name, this.age);
}
}

public WithDemo withAge(int age) {
return this.age == age ? this : new WithDemo(this.name, age);
}
}/<code>

3.12 其它特性

val

val 用在局部變量前面,相當於將變量聲明為 final,此外 Lombok 在編譯時還會自動進行類型推斷。val 的使用示例:

<code>public class ValExample {
public String example {
val example = new ArrayList<string>;
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase;
}

public voidexample2 {
val map = new HashMap<integer>;
map.put(0, "zero");
map.put(5, "five");
for (val entry : map.entrySet) {
System.out.printf("%d: %s\\n", entry.getKey, entry.getValue);
}
}
}/<integer>/<string>/<code>

以上代碼等價於:

<code>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 voidexample2 {
final HashMap<integer> map = new HashMap<integer>;
map.put(0, "zero");
map.put(5, "five");
for (final Map.Entry<integer> entry : map.entrySet) {
System.out.printf("%d: %s\\n", entry.getKey, entry.getValue);
}
}
}/<integer>/<integer>/<integer>/<string>/<string>/<code>

至此功能強大的 Lombok 工具就介紹完了。若你對於它的實現原理感興趣的話,建議閱讀猿碼道大佬 十分鐘搞懂Lombok使用與原理 這篇文章:

https://juejin.im/post/5a6eceb8f265da3e467555fe

作者 :semlinker

鏈接:https://segmentfault.com/a/1190000020864572

項目地址:Github - springboot2-lombok

四、參考資源

  • Lombok 官網

  • lombok-cheatsheet

  • Java開發神器Lombok的使用與原理

-END-

如果看到這裡,說明你喜歡這篇文章,請轉發、點贊微信搜索「web_resource」,關注後回覆「進群」或者掃描下方二維碼即可進入無廣告交流群。

效率提高 10 倍!一份不可多得的 Lombok 学习指南
效率提高 10 倍!一份不可多得的 Lombok 学习指南


分享到:


相關文章: