Lombok使用介紹

Lombok

是什麼

Lombok是一個經過註解的形式或簡單關鍵字簡化和消除Java應用程序中一些必須可是重複或顯得臃腫的樣板代碼的實用工具,使用Lombok會在編譯階段根據相應的註解生成對應的字節碼,使編譯前的源碼看起來更加簡潔,但功能不變。java

缺點是增長了依賴和學習Lombok的成本,還有必定程度上對代碼的可讀性形成影響。apache

怎麼用

  • 安裝:Lombok須要經過插件的形式與IDE集成,若是使用IntelliJ IDEA可直接到插件倉庫搜索Lombok進行安裝,若是使用Eclipse,首先須要下載lombok.jar(https://www.projectlombok.org/download),而後在CMD下執行命令:java -jar lombok.jar,等待掃描出本機安裝的eclipse後進行安裝確認,最後重啓eclipse便可。 還有一種安裝方式在直接拷貝lombok.jar到eclipse.ini的同級目錄下,而後編輯eclipse.ini,在最後一行加上-javaagent:lombok.jar,最後重啓eclipse。緩存

  • 使用:要在項目中使用Lombok,首先要在項目中引入lombok的依賴,從新編譯源代碼。框架

<dependency>
	<groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
	<version>1.16.18</version>
</dependency>

基本特性

註解/關鍵字 可以使用位置 說明
val 局部變量 簡化局部變量聲明的類型
@NonNull 字段、方法、入參、本地變量 生成檢查NullPointException代碼
@Cleanup 可關閉資源的本地變量對象,且銷燬方法沒有參數 簡化資源清理回收的代碼,消除try-catch-finally代碼塊
@Getter/@Setter 字段、枚舉常量、接口、類、枚舉、註解 簡化getter、setter代碼
@ToString 接口、類、枚舉、註解 自動生成toString方法
@EqualsAndHashCode 接口、類、枚舉、註解 自動生成equals方法和hashCode方法
@NoArgsConstructor 接口、類、枚舉、註解 生成無參構造函數
@RequiredArgsConstructor 接口、類、枚舉、註解 生成全部標識爲@NonNull的成員屬性的構造函數
@AllArgsConstructor 接口、類、枚舉、註解 生成包含全部成員屬性的構造函數
@Data 接口、類、枚舉、註解 是@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor的組合效果
@Value 接口、類、枚舉、註解 相似於@Data,區別在於字段會轉換爲final類型,且沒有setter方法
@NonFinal 字段、方法、方法參數、本地變量、註解、接口、類、枚舉 用來取消因使用@FieldDefaults和@Value而加上的final修飾符
@SneakyThrows 方法、構造函數 粗粒度的try-catch
@Synchronized 方法 做用等同於synchronized關鍵字,可自定義鎖對象
@Log 接口、類、枚舉、註解 簡化定義日誌記錄器對象的代碼,根據日誌框架的不一樣選擇不一樣的Log註解
  • val

val用來簡化局部變量聲明的類型,與Java10中的var關鍵字相似,都是從初始化表達式中推斷出變量的聲明類型,起到本地類型推斷的做用。須要注意的是val修飾的變量都會變成final類型,其引用不可更改。eclipse

val example = new ArrayList<String>();
example.add("hello");
example.add("lombok");
val element = example.get(0);

等價於:ide

final ArrayList<String> example = new ArrayList<String>();
example.add("hello");
example.add("lombok");
final String element = example.get(0);
  • @NonNull

@NonNull註解經常使用於加在方法和構造函數的入參上,它會幫助咱們生成檢查NullPointerException的代碼.函數

public NonNullExample(@NonNull Person person) {
    this.name = person.getName();
}

等價於:工具

public NonNullExample(@NonNull Person person) {
    if(person == null) {
        throw new NullPointException("person");
    }
    this.name = person.getName();
}
  • @Cleanup

@Cleanup註解用來簡化資源清理回收的代碼,確保指定的資源在退出當前代碼執行範圍前進行自動清理,消除常見的try-catch-finally代碼樣板,做用等同於try-with-resource,不過須要注意@Cleanup只能指定沒有參數的資源銷燬方法,若是銷燬方法有入參則不能使用@Cleanup註解。學習

public static void tradition() {
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream("test.txt");
			out = new FileOutputStream("output.txt");
			byte[] buffer = new byte[1024];
			int begin = 0;
			while (true) {
				int len = in.read(buffer);
				if (len == -1)
					break;
				out.write(buffer, begin, len);
				begin += len;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void tryWithResource() {
		try (InputStream in = new FileInputStream("test.txt"); 
			OutputStream out = new FileOutputStream("output.txt")) {
			byte[] buffer = new byte[1024];
			int begin = 0;
			while (true) {
				int len = in.read(buffer);
				if (len == -1)
					break;
				out.write(buffer, begin, len);
				begin += len;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void cleanUp() {
		try {
			@Cleanup InputStream in = new FileInputStream("test.txt");
			@Cleanup OutputStream out = new FileOutputStream("output.txt");
			byte[] buffer = new byte[1024];
			int begin = 0;
			while (true) {
				int len = in.read(buffer);
				if (len == -1)
					break;
				out.write(buffer, begin, len);
				begin += len;
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
  • @Getter/@Setter

@Getter和@Setter分別用來簡化getter和setter樣板代碼,默認生成的getter、setter方法修飾符爲public,若是須要指定方法的訪問範圍,能夠設置AccessLevel屬性,如:ui

@Getter @Setter(AccessLevel.PROTECTED) private String password;

另外,@Getter註解還有一個lazy=true的屬性,設置了該屬性會使咱們調用getter方法時才真正去計算獲取到的值,而且將第一次計算後的結果緩存下來,以後的調用直接返回該緩存值。

@Getter(lazy = true)
private final double[] cached = expensive();

private double[] expensive() {
	long begin = System.currentTimeMillis();
	double[] result = new double[5];
	for (int i = 0; i < result.length; i++) {
		result[i] = Math.asin(i);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	System.out.println((System.currentTimeMillis() - begin) / 1000);
	return result;
}

public static void main(String[] args) {
	GetterLazyExample example = new GetterLazyExample();
	System.out.println(example.getCached());
	System.out.println(example.getCached());
}

等價於:

private final AtomicReference<Object> cached = new AtomicReference<>();

public double[] getCached() {
	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() {
	long begin = System.currentTimeMillis();
	double[] result = new double[5];
	for (int i = 0; i < result.length; i++) {
		result[i] = Math.asin(i);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	System.out.println((System.currentTimeMillis() - begin) / 1000);
	return result;
}

public static void main(String[] args) {
	GetterLazyExample_Src example = new GetterLazyExample_Src();
	System.out.println(example.getCached());
	System.out.println(example.getCached());
}
  • @ToString

@ToString 用來自動生成toString方法,默認的toString方法會打印出類名和字段屬性和值,若是須要排除指定字段能夠用exclude='字段名'的方式進行排除;若是要嵌套調用父類的toString方法,則加上callSuper=true,includeFieldNames=true等屬性。

// @ToString // 默認打印類名、每一個字段名=值,用逗號分隔
// @ToString(exclude="password") //exclude屬性指定排除哪些字段
@ToString(callSuper = true,includeFieldNames=true)
public class ToStringExample extends Parent {

	@Getter
	@Setter
	private String name;
	@Getter
	@Setter
	private String password;
	@Getter
	@Setter
	private int age;

	public static void main(String[] args) {
		System.out.println(new ToStringExample());
	}

}

@ToString
class Parent {
	@Getter
	@Setter
	private String address;
	@Getter
	@Setter
	private String city;
}
  • @EqualsAndHashCode

@EqualsAndHashCode用來從字段中自動生成equals和hashCode方法,默認狀況下使用的是全部非靜態字段,也可使用exclude屬性排除指定的字段。

@EqualsAndHashCode(exclude= {"name"})
public class EqualsAndHashCodeExample {

	@Getter @Setter private String name;
	@Getter @Setter private int age;
	@Getter @Setter private double weight;

	public static void main(String[] args) {
		EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample();
		example1.setName("小明");
		example1.setAge(10);
		
		EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample();
		
		example2.setName("小紅");
		example2.setAge(10);
		
		System.out.println(example1.hashCode());
		System.out.println(example2.hashCode());
		System.out.println(example1.equals(example2));
	}
}
  • @NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor

@NoArgsConstructor用來生成無參構造函數。

@NoArgsConstructor
public class NoArgsConstructorExample {
	@Getter @Setter private String name;
}

等價於:

public class NoArgsConstructorExample {
    private String name;
    public NoArgsConstructorExample() {
        //public無參構造器
    }
    //省略getter、setter方法
    ......
}

@RequiredArgsConstructor用來生成包含全部修飾爲@NonNull的成員屬性的構造函數。

@RequiredArgsConstructor
public class RequiredArgsConstructorExample {
	@Getter @Setter @NonNull private String name;
	@Getter @Setter private String password;
	@Getter @Setter @NonNull private Character sex;
}

等價於:

public class RequiredArgsConstructorExample {
    private String name;
    private String password;
    private Character sex;
    
    private RequiredArgsConstructorExample(String name, Character sex) {
        if(name == null) {
            throw new NullPointerException("name");
        }
        if(sex == null) {
            throw new NullPointerException("sex");
        }
        this.name = name;
        this.sex = sex;
    }
    
    //省略getter、setter方法
    ......
}

@AllArgsConstructor用來生成一個包含全部變量的公有構造函數。

@AllArgsConstructor
public class AllArgsContructorExample {
	@Getter @Setter private String name;
	@Getter @Setter private Integer age;
	@Getter @Setter private String address;
}

等價於:

public class AllArgsContructorExample {
    private String name;
    private Integer age;
    private String address;
    
    public AllArgsContructorExample(String name, Integer age, String address) {
        this.name = name,
        this.age = age;
        this.address = address;
    }
    
    //省略getter、setter方法
    ......
}
  • @Data

@Data是一個簡單粗暴的組合註解,使用@Data註解至關於同時使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor這幾個註解

@Data
public class DataExample {
	
	private String name;
	private int age;
	private String password;

}
  • @Value

@Value跟@Data相似,區別在於若是變量不加@NonFinal修飾,@Value會將字段變成final類型,同時也沒有setter方法。

  • @NonFinal

修飾字段,用來取消因使用@FieldDefaults和@Value而加上的final修飾符。

@Value
public class NonFinalExample {
	private String id; //final
	private String name; //final
	@NonFinal private String password; //非final
}
  • @Builder

@Builder簡化了普通的建造者模式API,能夠用在類、構造器、方法上,若是字段屬於集合類型,加上@Singular,會生成兩個向集合中添加單一元素和全部元素的方法,以及一個清除集合的方法。

@Builder
 public class Example {
        private int foo;
        private final String bar;
 }

等價於

public class Example<T> {
    private T foo;
    private final String bar;

    private Example(T foo, String bar) {
            this.foo = foo;
            this.bar = bar;
    }

    public static <T> ExampleBuilder<T> builder() {
            return new ExampleBuilder<T>();
    }

    public static class ExampleBuilder<T> {
            private T foo;
            private String bar;

            private ExampleBuilder() {}

            public ExampleBuilder foo(T foo) {
                    this.foo = foo;
                    return this;
            }

            public ExampleBuilder bar(String bar) {
                    this.bar = bar;
                    return this;
            }

            @java.lang.Override 
            public String toString() {
                    return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
            }

            public Example build() {
                    return new Example(foo, bar);
            }
    }
}
  • @SneakyThrows

@SneakyThrows註解用在方法和構造函數上,它會將方法中的全部代碼用try-catch語句包裹起來,當捕獲到異常後經過Lombok.sneakyThrow(e)將原始異常拋出,不過須要注意的是調用該方法的Client端並不知道會拋出哪一種異常,即便這是一個CheckException。

public class SneakyThrowsExample {

	@SneakyThrows(UnsupportedEncodingException.class)
	public static String utf8ToString(byte[] bytes) {
		return new String(bytes, "UTF-8");
	}
	
	public static void main(String[] args) {
		String str = SneakyThrowsExample.utf8ToString("hello lomboks".getBytes());
		System.out.println(str);
	}
}
  • @Synchronized

@Synchronized註解用在方法上,做用等同於synchronized關鍵字,區別在於鎖對象不一樣,對於synchronized關鍵字,修飾類方法時鎖對象是class對象,修飾成員方法時鎖對象是this對象,而使用@synchronized註解時鎖對象分別是私有靜態變量LOCK和私有final對象lock,也能夠本身指定鎖對象。

public class SynchronizedExample {
	
	private final Object readLock = new Object();
	
	@Synchronized("readLock")
	@SneakyThrows
	public void read() {
		System.out.println(Thread.currentThread().getName() + " read");
		Thread.sleep(3000);
	}
	
	public static void main(String[] args) {
		SynchronizedExample example = new SynchronizedExample();
		new Thread(()->example.read()).start();
		new Thread(()->example.read()).start();
	}

}
  • Log註解:@CommonsLog、@Log、@Log4j、@Log4j二、@Slf4j、@XSl4j、@JBossLog

Log註解能夠省去從日誌工廠生成日誌記錄器對象的代碼,可使用topic指定生成log對象時的類名,根據項目中使用的日誌框架不一樣,有不一樣的註解能夠選擇。

@CommonsLog(topic="LogExample")
//等價於
org.apache.commons.logging.LogFactory.getLog(LogExample.class);

@Log(topic="LogExample")
//等價於
java.util.loggin.Logger.getLogger(LogExample.class);

@Log4j(topic="LogExample")
//等價於
org.apache.log4j.Logger.getLogger(LogExample.class);

@Log4j2(topic="LogExample")
//等價於
org.apache.loggin.log4j.LogManager.getLoggerr(LogExample.class);

@Slf4j(topic="LogExample")
//等價於
org.slf4j.LoggerFactory.getLogger(LogExample.class);

@XSLf4j(topic="LogExample") 
//等價於
org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

@JBossLog(topic="LogExample") 
//等價於
org.jboss.logging.Logger.getLogger(LogExample.class);

實驗性特性

除了以上經常使用的基本功能外,Lombok還有部分實驗性質的特性沒有正式推薦使用,有些可能違背了對Java的常規認知或者只支持部分開發環境,因此不推薦使用。

  • @Accessors

@Accessors是一個爲getter和setter設計的更爲流暢的API,當設置了chain=true屬性,會自動生成符合鏈式調用的setter方法,返回this引用代替默認的void方法,若是設置flunt=true,則不會有get/set前綴,一樣支持鏈式調用。

@Accessors(chain=true)
@Data
public class AccessorsExample {

	private String id;
	private String name;
	private String address;
	
	public static void main(String[] args) {
		AccessorsExample example = new AccessorsExample();
		example.setId("123").setName("小明").setAddress("深圳");
	}
}
  • @Delegate

@Delegate可以將一個對象的某些方法代理給另外一個對象。

public class DelegateExample {

	private interface DelegateMethod { //該接口的方法必須是collection擁有的方法
		boolean add(String item);
		int size();
	}
	
    @Delegate(types=DelegateMethod.class)               
    //聲明代理的接口,即將collection的某些方法代理給當前對象
	private final Collection<String> collection = new ArrayList<>();
	
	public static void main(String[] args) {
		DelegateExample example = new DelegateExample();
		example.add("element"); //實際上添加到了collection中
		System.out.println(example.size());
	}
}
  • @ExtensionMethod

@ExtensionMethod能夠Wie已經存在的類增長方法,可是這種方式在代碼風格上衝擊較大、在運行時沒有好的辦法讓其餘類引用,部分開發環境如netbeans下不可用等等,所以不建議使用。

@Data
@ExtensionMethod({DateUtils.class}) //擴展的方法來自DateUtils類
public class ExtensionMehtodExample {
	
	public void test() {
		Date date = new Date();
		String str = date.formate();
		System.out.println(str);
	}
	
	public static void main(String[] args) {
		new ExtensionMehtodExample().test();
	}
	
}

class DateUtils {
	public static final String formate(Date date) { //必須是static方法
		DateFormat formator = new SimpleDateFormat("HH:mm:ss");
		return formator.format(date);
	}
}
  • @FieldDefaults

設置默認的字段修飾符,即若是字段不加修飾符則採用FieldDefaults註解設置的修飾符.

@FieldDefaults(level = AccessLevel.PRIVATE)
@Data
public class FieldDefaultsExample {

	String id;
	String name;
	protected String address;

}
  • @Helper

修飾在包含局部類的方法上,用來指示將局部類中的全部方法暴露給外圍方法,就好像它們是其輔助方法同樣。

public class HelperExample {

	public void doSomething(String name) {
		@Helper // 修飾局部類,將局部類中的全部方法暴露給外圍的方法
		class Helpers {
			void sayHello(String name) {
				System.out.println("hello " + name);
			}

			void sayGoodBye(String name) {
				System.out.println("Goodbye " + name);
			}
		}
		sayHello(name); // 直接使用局部類中的方法,等同於new Helpers().sayHello(name);
		sayGoodBye(name);
	}

	public static void main(String[] args) {
		new HelperExample().doSomething("小明");
	}
}
  • PackagePrivate

修飾字段,將當前字段設置爲訪問權限設置爲package-private,通常用來取消因使用@Value和@FieldDefaults而加上的修飾符。

@FieldDefaults(level = AccessLevel.PRIVATE)
@Data
public class PackagePrivateExample {
	String id; // public
	String name; // public
	@PackagePrivate String address; // package-private
}
  • @Tolerate

該註解用來解決某些狀況下使用Lombok註解生成的構造器或方法與開發者本身寫構造器或方法由於衝突而被跳過的狀況,將@Tolerate修飾在構造器/方法上,會被lombok視爲該構造器/方法不存在,典型的如當@Data和@Builder同時使用時Lombok生成構造器只有一個包含全部成員屬性的構造函數,若是再自定義一個無參構造函數將會衝突,此時可使用@Tolerate解決。

@Data
@Builder
public class TolerateExample {

	private String name;
	private String age;

	@Tolerate
	public TolerateExample() {
	}

}
  • @UtilityClass

建立工具類的註釋,當在類上加上該註解,該類會被修飾爲final類型,若是該類聲明瞭構造函數編譯器將會提示錯誤,不然會自動生成一個私有的構造函數,內部拋出一個UnsupportedOperationException異常。而且全部的方法、內部類和屬性都會被修飾爲static。

@UtilityClass
public class UtilityClassExample {

	private DateFormat df = new SimpleDateFormat("YYYY-MM-DD");
	
	public String formateToDay(Date date) {
		return df.format(date);
	}
	
}

等價於:

public class UtilityClassExample {

	private static DateFormat df = new SimpleDateFormat("YYYY-MM-DD");
	
	public static String formateToDay(Date date) {
		return df.format(date);
	}
	
	private UtilityClassExample() {
		throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
	}
	
}
  • @Wither

使用@Wither修飾字段X,會自動生成一個withX方法,該方法會生成該對象的一個克隆,該註解也可使用在類上,此時至關於全部非靜態字段都加上了@Wither註解。

public class WitherExample {
    @Wither private final int foo;
    private final String name;
    public WitherExample(int foo, String name) {
        this.foo = foo;
        this.name = name;
    }
}

等價於:

public class WitherExample {
    private final int foo;
    private final String name;
    public WitherExample(int foo, String name) {
        this.foo = foo;
        this.name = name;
    }
    public WitherExample withFoo(int foo) { //傳入的foo相同返回當前對象,不然返回除foo字段不一樣的一個克隆對象
        return this.foo == foo ? this ? new WitherExample(foo, this.name);
    }
}
  • var

與val關鍵字相似,一樣起到本地類型推斷的做用,區別在於var修飾的變量不會轉變爲final類型,而val修飾的變量都會變成final類型。

相關文章
相關標籤/搜索