Lombok使用詳解

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的過程中,它產生作用的具體流程如下:

  1. javac對源代碼進行分析,生成一棵抽象語法樹(AST)

  2. javac編譯過程中調用實現了JSR 269的Lombok程序

  3. 此時Lombok就對第一步驟得到的AST進行處理,找到Lombok註解所在類對應的語法樹 (AST),然後修改該語法樹(AST),增加Lombok註解定義的相應樹節點

  4. 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標記每個要打印的字段。

  • 通過設置callSupertrue,可以將超類實現的 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修飾的,所有字段由privatefinal修飾,每個字段會生成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塊加鎖的對象一定是類中的一個成員屬性,可以通過@Synchronizedvalue指定,如果不存在則由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");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章