天天看點

Lombok之使用詳解

前言

  在Java中,封裝是一個非常好的機制,最常見的封裝莫過于get,set方法了,無論是Intellij idea 還是Eclipse,都提供了快速生成get,set方法的快捷鍵,使用起來很是友善,其實,我們還有更友善的辦法,那就是-Lombok:非常強大的POJO注解器。

Lombok是什麼?

  lombok 提供了簡單的注解的形式來幫助我們簡化消除一些必須有但顯得很臃腫的 java 代碼。特别是相對于 POJO。

如何安裝Lombok?

  使用 lombok 是需要安裝的,如果不安裝,IDE 則無法解析 lombok 注解。先在官網下載下傳最新版本的 JAR 包。

  1. 輕按兩下下載下傳下來的 JAR 包安裝 lombok;

    我選擇這種方式安裝的時候提示沒有發現任何 IDE,是以我沒安裝成功,我是手動安裝的。

  2. eclipse / myeclipse 手動安裝 lombok(Mac 下eclipse安裝Lombok插件)
    1. 将 lombok.jar 複制到 myeclipse.ini / eclipse.ini 所在的檔案夾目錄下
    2. 打開 eclipse.ini / myeclipse.ini,在最後面插入以下兩行并儲存: 
      -Xbootclasspath/a:lombok.jar
      -javaagent:lombok.jar      
    3. 重新開機 eclipse / myeclipse

Lombok使用詳解

  • 添加POM依賴:

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.0</version>
    </dependency>      
  • Lombok提供注解方式來提高代碼的簡潔性,常用注解概覽:

    • @Data:注解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法,相當于同時加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode
    • @Setter、@Getter:注解在類和屬性上;為屬性提供 setting、getting 方法
    • @ToString:生成toString方法,預設情況下,會輸出類名、所有屬性,屬性按照順序輸出,以逗号分割。
    • @EqualsAndHashCode:實作equals()方法和hashCode()方法
    • @Builder:建構 建造者模式
    • @NonNull:該注解快速判斷是否為空,如果為空,則抛出java.lang.NullPointerException
    • @Synchronized:該注解自動添加到同步機制,有趣的是,生成的代碼并不是直接鎖方法,而是鎖代碼塊, 作用範圍是方法上
    • @Log4j :注解在類上;為類提供一個 屬性名為log 的 log4j 日志對象
    • @NoArgsConstructor:注解在類上;為類提供一個無參的構造方法
    • @RequiredArgsConstructor:注解在類上;為類提供一個部分參的構造方法(使用類中所有帶有@NonNull注解的或者帶有final修飾的成員變量生成對應的構造方法)
    • @AllArgsConstructor:注解在類上;為類提供一個全參的構造方法
    • @Cleanup:用于確定已配置設定的資源被釋放,如IO的連接配接關閉
    • @SneakyThrows:抛異常
    • @Accessors(chain = true):使用鍊式結構

@Data

  注解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法,相當于同時加上以下注解@Setter @Getter,@ToString,@EqualsAndHashCode

1 @Data
2 public class Person {
3     private String name;
4     private String address;
5     private String city;
6     private String state;
7     private String zip;
8     private Date brithday;
9 }      

效果如下:

@Getter@Setter

  注解在類和屬性上;為屬性提供 setting、getting 方法

public class Person {
    @Getter@Setter
    private String name;
}      

  等價源碼:

1 public String getName() {
2         return name;
3     }
4  
5 public void setName(String name) {
6         this.name = name;
7 }      

@ToString

生成toString方法,預設情況下,會輸出類名、所有屬性,屬性按照順序輸出,以逗号分割。但需要注意的是:@ToString有多個屬性可以進一步設定:

    • callSuper 是否輸出父類的toString方法,預設為false
    • includeFieldNames 是否包含字段名稱,預設為true
    • exclude 排除生成tostring的字段

      使用方法:

      1 @ToString(callSuper = true,exclude ={"name"})
      2 public class Person {
      3     private String name;
      4     private String address;
      5 }      
      等價源碼:
      1 public String toString() {
      2  return "Person{" +
      3                 "address=\'" + address + \'\\'\' +
      4     \'}\';
      5 }      

@NonNull

該注解快速判斷是否為空,如果為空,則抛出java.lang.NullPointerException

  使用方法

1 public class Person {
2  
3     private String name;
4     
5     @Setter@Getter@NonNull
6     private List<Person> member;
7 }      

  等價源碼:

1 @NonNull
 2 private List<Person> members;
 3  
 4 public Family(@NonNull final List<Person> members) {
 5     if (members == null) throw new java.lang.NullPointerException("members");
 6     this.members = members;
 7 }
 8     
 9 @NonNull
10 public List<Person> getMembers() {
11     return members;
12 }
13  
14 public void setMembers(@NonNull final List<Person> members) {
15     if (members == null) throw new java.lang.NullPointerException("members");
16     this.members = members;
17 }      

@Synchronized

該注解自動添加到同步機制,有趣的是,生成的代碼并不是直接鎖方法,而是鎖代碼塊, 作用範圍是方法上。

使用方法:

1 private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
2  
3 @Synchronized
4 public String synchronizedFormat(Date date) {
5     return format.format(date);
6 }      

等價源碼:

1 private final java.lang.Object $lock = new java.lang.Object[0];
2 private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
3  
4 public String synchronizedFormat(Date date) {
5     synchronized ($lock) {
6         return format.format(date);
7     }
8 }      

@Cleanup

注釋可用于確定已配置設定的資源被釋放,如IO的連接配接關閉。

使用方法:

1 public void testCleanUp() {
2     try {
3         @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
4         baos.write(new byte[] {\'Y\',\'e\',\'s\'});
5         System.out.println(baos.toString());
6     } catch (IOException e) {
7         e.printStackTrace();
8     }
9 }      

等價源碼:

1 public void testCleanUp() {
 2     try {
 3         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 4         try {
 5             baos.write(new byte[]{\'Y\', \'e\', \'s\'});
 6             System.out.println(baos.toString());
 7         } finally {
 8             baos.close();
 9         }
10     } catch (IOException e) {
11         e.printStackTrace();
12     }
13 }      

@Accessors(chain = true)

  使用鍊式結構

  • 使用方法:
    @Accessors(chain=true)
    public class Student {
            private String name;
            private int age;
    
            public String getName() {
                return name;
            }
    
            public Student setName(String name) {
                this.name = name;
                return this;
            }
    
            public int getAge() {
                return age;
            }
    
            public Student setAge(int age) {
                return this;
            }
        }      
  • 等價源碼:
    1 @Accessors(chain = true)
    2     @Data
    3     @NoArgsConstructor(staticName = "of")
    4     public class Student {
    5         private String name;
    6         private int age;
    7     }      
  • 調用
    1 Student student = Student.of().setName("wsk").setAge(12);      

@Builder

  使用builder,建構 建造者模式

  • 例一:
    • 使用@Builder
      1 @Builder
      2     public class Student {
      3         private String name;
      4         private int age;
      5     }      
    • 調用示例:
      1 Student student = Student.builder().name("zs").age(24).build();      
    • 等價源碼:
      1 public class Student {
       2         private String name;
       3         private int age;
       4 
       5         public String getName() {
       6                 return name;
       7         }
       8 
       9         public void setName(String name) {
      10                 this.name = name;
      11         }
      12 
      13         public int getAge() {
      14                 return age;
      15         }
      16 
      17         public void setAge(int age) {
      18                 this.age = age;
      19         }
      20 
      21         public static Builder builder(){
      22                 return new Builder();
      23         }
      24         public static class Builder{
      25                 private String name;
      26                 private int age;
      27                 public Builder name(String name){
      28                         this.name = name;
      29                         return this;
      30                 }
      31 
      32                 public Builder age(int age){
      33                         this.age = age;
      34                         return this;
      35                 }
      36 
      37                 public Student build(){
      38                         Student student = new Student();
      39                         student.setAge(age);
      40                         student.setName(name);
      41                         return student;
      42                 }
      43         }
      44     }      
  • 例二:利用builder模式設計的Java類  
    • 如果能将建立JavaBean和設定内容揉在一起,在傳入builder中的參數不合乎業務或者非法,那麼就不能建立student對象,這時候可以通過捕獲IllegalArgumentException,進而得知失敗的原因;
    • 引入Builder設計模式以後,代碼保持JavaBean好的可讀性,但同時增強了安全性,将Student類的建立和設定内容揉在了一起,并增加了安全性檢查,提高了系統的健壯性,同時防止了編碼中的一些疏忽。
    • Java示例:
      1 public class Student {
       2     private String id;
       3     private String name;
       4     private String sex;
       5     private int age;
       6     private String department;
       7 
       8     public static class Builder {
       9         /*
      10          * 隻能指定一次。
      11          */
      12         private final String id;
      13         private final String department;
      14 
      15         private String name = "";
      16         private String sex = "男";
      17         private int age = 20;
      18 
      19         /*
      20          * 非空屬性,必須在構造器中指定。
      21          */
      22         public Builder(String id, String department) {
      23             this.id = id;
      24             this.department = department;
      25         }
      26 
      27         /*
      28          * name,sex,age可選擇屬性,提供特殊的setter方法。
      29          */
      30         public Builder name(String name) {
      31             this.name = name;
      32             return this;
      33         }
      34 
      35         public Builder sex(String sex) {
      36             this.sex = sex;
      37             return this;
      38         }
      39 
      40         public Builder age(int age) {
      41             this.age = age;
      42             return this;
      43         }
      44 
      45         /*
      46          * Student對象建立器,想得到一個Student對象必須使用build 方法,
      47          * 在方法中增加對Builder參數的驗證,并以異常的形式告訴給開發人員。
      48          */
      49         public Student build() {
      50             /* 檢查Builder對象中的資料是否合法。
      51              * 針對這個例子,就是檢查主鍵沖突,外鍵制約等
      52              * 如果不滿足我們可以抛出一個IllegalArgumentException
      53              */
      54             return new Student(this);
      55             
      56         }
      57         
      58     }
      59 
      60     private Student(Builder builder) {
      61         this.id = builder.id;
      62         this.name = builder.name;
      63         this.sex = builder.sex;
      64         this.age = builder.age;
      65         this.department = builder.department;
      66     }
      67 
      68     /*
      69      * 隻提供getter方法
      70      */
      71     public String getId() {
      72         return id;
      73     }
      74 
      75     public String getName() {
      76         return name;
      77     }
      78 
      79     public String getSex() {
      80         return sex;
      81     }
      82 
      83     public int getAge() {
      84         return age;
      85     }
      86 
      87     public String getDepartment() {
      88         return department;
      89     }
      90 
      91 }      
    • 建立對象一:
      1      student = new Student.Builder("03041013", "計算機").name("李華").build();      
    • 建立對象二:
      1     Student.Builder builder = new Student.Builder("03041013", "計算機");
      2     builder.name("李華");
      3     Student student = builder.build();      

 小結:

  • 很明顯,使用 lombok 要簡潔許多,特别是在類的屬性較多的情況下;
  • 同時也避免了修改字段名字時候忘記修改方法名所犯的低級錯誤;

參考資料

  • GitHub:https://github.com/rzwitserloot/lombok
  • 官網:https://projectlombok.org/
  • Reducing Boilerplate Code with Project Lombok
Lombok之使用詳解