1. 程式人生 > >Java神奇工具——Lombok[最全]

Java神奇工具——Lombok[最全]

最近剛接觸,結果官網訪問速度很慢,網上部落格又沒有介紹全,打算自己做個詳細的,以防後面複習使用。

目錄

 

Lombok

如何用Lombok——兩種方法

有哪些註解可以使用呢[1.16.18版本一共18個註解,有一個註解不推薦使用]

@AllArgsConstructor / @NoArgsConstructor / @RequiredArgsConstructor

@Builder

@ Builder.Default

@Singular

@Cleanup

@Data

@Delegate

@EqualsAndHashCode

@Generated

@Getter / @Setter

@NonNull

@SneakyThrows

@Synchronized

@ToString

@log

val

@Value


Lombok

這個是Java一個神奇工具,使用註解,減少Java的冗餘程式碼。最低支援Jdk1.6【我用的是Jdk1.8】

官網:https://www.projectlombok.org

github原始碼地址:https://github.com/rzwitserloot/lombok【又是個胖胖的大佬寫的】

官網上對此也有介紹:Lombok專案是一個Java庫, 它可以自動插入你的編輯器並構建工具,為你的Java程式新增興趣。永遠不需要再寫getter方法或者equals方法了。更早地去接觸Java地未來,就像接觸val一樣。

顯而易見,這個工具就是讓我們在建立實體類的時候,變得簡單,輕鬆。

如何用Lombok——兩種方法

1.如果你是IDEA,可以直接安裝外掛

ideaå®è£Lombok

2.如果你是Maven專案,可以新增到pom.xml檔案中

中央倉庫選擇版本:https://mvnrepository.com/artifact/org.projectlombok/lombok

這邊使用人數最多的1.16.18版本

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <scope>provided</scope>
</dependency>

有哪些註解可以使用呢[1.16.18版本一共18個註解,有一個註解不推薦使用]

 @AllArgsConstructor會生成全參構造器。

@NoArgsConstructor會生成無參構造器。但是對於具有約束的欄位(如@NonNull欄位),不會生成任何檢查,因此請注意,在稍後正確初始化這些欄位之前,通常不會滿足這些約束。某些java構造(例如hibernate和Service Provider Interface)需要no-args建構函式。此批註主要與@Data生成註釋的其他建構函式之一或其中一個配合使用。

@RequiredArgsConstructor會將未初始化的final變數和帶@NonNull的變數作為構造器引數。

 

  • @AllArgsConstructor / @NoArgsConstructor / @RequiredArgsConstructor

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

相當於:

public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  
  private ConstructorExample(T description) {
    if (description == nullthrow 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 == nullthrow new NullPointerException("description");
    this.x = x;
    this.y = y;
    this.description = description;
  }
  
  public static class NoArgsExample {
    @NonNull private String field;
    
    public NoArgsExample() {
    }
  }
}
  • @Builder

@Builder 允許您使用以下程式碼自動生成使您的類可例項化所需的程式碼:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();

@Builder可以放在類,建構函式或方法上。雖然“在類上”和“在建構函式上”模式是最常見的用例,但@Builder最容易用“方法”用例來解釋。

@Builder(從現在開始呼叫目標)註釋的方法會導致生成以下7件事:

深入瞭解網址:https://projectlombok.org/features/Builder

 

  • 一個名為的內部靜態類FooBuilder,具有與靜態方法相同的型別引數(稱為構建器)。
  • 構建器中目標的每個引數的一個私有非靜態非最終欄位。
  • 構建器中:一個包私有no-args空建構函式。
  • 構建器中:對於目標的每個引數,類似於“setter”的方法:它具有與該引數相同的型別和相同的名稱。它返回構建器本身,以便可以連結setter呼叫,如上例所示。
  • 構建器中build()呼叫方法的方法,傳入每個欄位。它返回與目標返回的相同型別。
  • 構建器中:一個明智的toString()實現。
  • 在包含目標的類中:一種builder()方法,它建立構建器的新例項。

@ Builder.Default

如果在構建會話期間從未設定某個欄位/引數,則它始終為0 / null/ false。如果您已經放置@Builder了一個類(而不是方法或建構函式),則可以直接在該欄位上指定預設值,並使用以下內容對該欄位進行註釋@Builder.Default
@Builder.Default private final long created = System.currentTimeMillis();

@Singular

通過使用註釋註釋其中一個引數(如果使用方法或建構函式進行註釋@Builder)或欄位(如果使用註釋類@Builder@Singular,lombok將該構建器節點視為集合,並生成2個“加法器”方法而不是“ setter'方法。一個向集合新增單個元素,另一個將另一個集合的所有元素新增到集合中。將不生成僅設定集合(替換已新增的任何內容)的setter。還生成了“清晰”方法。這些“單一”構建器非常複雜,以保證以下屬性:

  • 呼叫時build(),生成的集合將是不可變的。
  • 在呼叫之後呼叫“adder”方法之一或“clear”方法build()不會修改任何已生成的物件,並且如果build()稍後再次呼叫,則會生成自生成構建器以來添加了所有元素的另一個集合。
  • 生成的集合將被壓縮到最小的可行格式,同時保持高效。

@Singular只能應用於lombok已知的集合型別。目前,支援的型別是:

  • java.util
    • IterableCollectionListArrayList在一般情況下由壓縮的不可修改的支援)。
    • SetSortedSetNavigableSet(由一個適當的大小不可修改HashSetTreeSet在一般情況下支援)。
    • MapSortedMapNavigableMap(由一個適當的大小不可修改HashMapTreeMap在一般情況下支援)。
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;
}

相當於:

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

您可以使用@Cleanup以確保在程式碼執行路徑退出當前作用域之前自動清除給定資源。您可以通過使用註釋註釋任何區域性變數宣告來執行此操作@Cleanup@Cleanup InputStream in = new FileInputStream("some/file");  

 

  • @Cleanup

import lombok.Cleanup;
import java.io.*;

public class CleanupExample {
  public static void main(String[] argsthrows 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 == -1break;
      out.write(b, 0, r);
    }
  }
}

相當於

import java.io.*;

public class CleanupExample {
  public static void main(String[] argsthrows 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 == -1break;
          out.write(b, 0, r);
        }
      finally {
        if (out != null) {
          out.close();
        }
      }
    finally {
      if (in != null) {
        in.close();
      }
    }
  }
}

@Data是一個方便的快捷方式註釋,它捆綁了@ToString@EqualsAndHashCode@Getter/@Setter@RequiredArgsConstructor它們的特徵:換句話說,@Data生成通常與簡單POJO(普通舊Java物件)和bean相關聯的所有樣板:所有欄位的getter,所有非的setter最終場,和適當的toStringequalshashCode實現涉及類的欄位和初始化所有final欄位,以及不具有初始已打上所有非最終場構造@NonNull,以保證該領域從來都不是空值。

 

  • @Data

import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;

@Data public class DataExample {
  private final String name;
  @Setter(AccessLevel.PACKAGEprivate 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;
  }
}

 相當於:

 

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 == thisreturn true;
    if (!(instanceof DataExample)) return false;
    DataExample other = (DataExampleo;
    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()) != 0return 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*PRIMEthis.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 == thisreturn true;
      if (!(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;
    }
  }
}

 

  • @Delegate

NEW IN Lombok 0.10: Any field or no-argument method can be annotated with @Delegate to let lombok generate delegate methods that forward the call to this field (or the result of invoking this method).

Lombok delegates all public methods of the field's type (or method's return type), as well as those of its supertype except for all methods declared in java.lang.Object.

You can pass any number of classes into the @Delegate annotation's types parameter. If you do that, then lombok will delegate all public methods in those types (and their supertypes, except java.lang.Object) instead of looking at the field/method's type.

All public non-Object methods that are part of the calculated type(s) are copied, whether or not you also wrote implementations for those methods. That would thus result in duplicate method errors. You can avoid these by using the @Delegate(excludes=SomeType.class) parameter to exclude all public methods in the excluded type(s), and their supertypes.

To have very precise control over what is delegated and what isn't, write private inner interfaces with method signatures, then specify these private inner interfaces as types in @Delegate(types=PrivateInnerInterfaceWithIncludesList.class, excludes=SameForExcludes.class).

 

  • @EqualsAndHashCode

  • @Generated

  • @Getter / @Setter

  • @NonNull

您可以在方法或建構函式的引數上使用@NonNull讓Lombok自動進行非空檢測。 

import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

相當於: 

 

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

@SneakyThrows可以用來偷偷丟擲已檢查的異常而不在方法的throws子句中實際宣告這一點。當然,應該謹慎使用這種有爭議的能力。由lombok生成的程式碼不會忽略,包裝,替換或以其他方式修改丟擲的已檢查異常; 它只是偽造了編譯器。在JVM(類檔案)級別上,無論方法的throws子句如何,都可以丟擲所有異常(無論是否已檢查),這就是為什麼這樣做的原因。

當您想要選擇退出已檢查的異常機制時,常見的用例圍繞兩種情況:

 

  • 一個不必要的嚴格的介面,例如Runnable- 無論是否傳播出你的run()方法,檢查與否,它都將被傳遞給Thread未處理的異常處理程式。捕獲已檢查的異常並將其包裝在某種情況RuntimeException下只會模糊問題的真正原因。
  • 一個'不可能'的例外。例如,new String(someByteArray, "UTF-8");宣告它可以丟擲UnsupportedEncodingException但是根據JVM規範,UTF-8 必須始終可用。一個UnsupportedEncodingException在這裡大約是有可能的ClassNotFoundError,當你使用一個String物件,而你沒有捕獲這些異常。
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();
  }
}

 相當於:

 

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