優雅的Java工具庫Lombok


優雅的Java工具庫Lombok

最近在公司的項目中看到了對於Lombok的應用,通過@Data注解標注POJO,省略了大量的getter/setter代碼,原先冗長的POJO在瘦身之后直接變得干凈、清爽,程序員再也不需要去關注那些長長的方法,只需要集中注意力於字段field之中

Lombok簡介

Lombok是一個非常實用的Java工具庫,有效地簡化Java代碼的冗長。它通過注解如@Data可以直接為Java bean在編譯期動態地生成字段的getter/setter方法,使用注解@NoArgsConstructor 和@AllArgsConstructor 為Java bean添加無參構造器和有參構造器,甚至可以在Java代碼中使用val和var聲明一個動態變量,而無需再指定具體的變量類型,區別只是val聲明的變量為final。Lombok還提供了delombok供生成Javadoc,delombok在運行時會將注解@Data轉換成getter/setter方法,然后移除@Data注解,如果哪天不再需要Lombok,也只需要簡單運行delombok即可。Lombok的構建支持maven和gradle,同時eclipse、myeclipse和idea等主流IDE也都和lombok兼容,所以可以放心大膽地使用Lombok,不用擔心IDE的編譯檢查問題。

Lombok栗子

Eclipse安裝Lombok支持

官網Lombok https://projectlombok.org/download 下載jar包或者通過構建工具maven,gradle下載jar包

雙擊jar包,jar包內的安裝器會自動運行尋找eclipse

點擊【Install/Update】

引入Lombok依賴

	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>1.18.2</version>
		<scope>provided</scope>
	</dependency>

Lombok注解使用

Lombok的注解分為穩定版本和試驗版本,這里主要介紹穩定版本,因為試驗版本的支持目前和IDE不是很好

@Getter/@Setter注解

@Getter/@Setter注解的作用就是為字段添加getter/setter方法,可標注在類上,也可標注在字段上。標注在類上表示所有的非靜態(no-static)字段都會生成相應的getter/setter方法,標注在字段上表示只為這個字段生成,且會覆蓋標注在類上的注解。可設置訪問級別,默認為public。@Setter不可以標注final字段

@Getter@Setter
public class SetterExample {
	
	@Getter(value=AccessLevel.PRIVATE)@Setter
	private String name;
	
	//onMethod=@__({@AnnotationsHere})
	@Setter(onMethod=@__({@Deprecated}))
	private String age;
	
	//onParam=@__({@AnnotationsHere})
	@Setter(onParam=@__({}))
	private String sex;
	
	public static void main(String[] args) {
		SetterExample se = new SetterExample();
		se.setName("zhangsan");
		se.setAge("16");
		System.out.println(se.getAge());
		System.out.println(se.getName());
	}
}

Lombok提供了onX的試驗屬性,分別為:onMethod, onParam, onConstructor,用於向生成的方法,構造器,參數添加注解

反編譯后結果

@NonNull注解

@NonNull注解標注方法和構造器的參數,如果參數為null,則會拋出空指針異常,不需要在代碼中進行null檢測

public class NonNullExample {
	
	@Getter
	private String name;
	
	public NonNullExample(@NonNull String name){
		this.name = name;
	}
	
	public static void main(String[] args){
		String name = null;
		NonNullExample nne = new NonNullExample(name);
		System.out.println(nne.getName());
	}
}

@ToString注解

@ToString注解生成toString()方法

@ToString
public class ToStringExample {

	@ToString.Exclude
	private String name;
	
	@ToString.Include
	private String age;
	
	private String sex;
	
	public static void main(String[] args) {
		ToStringExample tse = new ToStringExample();
		System.out.println(tse.toString());
	}
}

屬性includeFieldNames,默認為true,包含屬性值

屬性callSuper,默認為false,調用父類實現

屬性onlyExplicitlyIncluded,默認為false,僅包含明確包含的屬性

@ToString.Exclude 標注屬性值不包含在toString()方法中

@ToString.Include標注屬性值包含在toString()方法中

@EqualsAndHashCode

@EqualsAndHashCode注解生成equals()和hashcode()方法,注解的屬性和@ToString類似

@EqualsAndHashCode
public class EqualsAndHashcodeExample {

	private String name;
	private String age;
	private String sex;
	
	public static void main(String[] args) {
		EqualsAndHashcodeExample ehe1 = new EqualsAndHashcodeExample();
		EqualsAndHashcodeExample ehe2 = new EqualsAndHashcodeExample();
		System.out.println(ehe1.equals(ehe2));
		System.out.println(ehe1.hashCode());
		System.out.println(ehe2.hashCode());
	}
}

@NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor

@NoArgsConstructor : 生成一個無參數的構造方法

@NoArgsConstructor(force=true, staticName="newInstance")
public class NoArgsConstructorExample {

	//包含的final字段如果沒有初始化,需要加上force=true強制初始化,否則編譯錯誤
	private final String name;
	
	//不會進行null檢查
	@NonNull
	@Getter
	private String age;
	
	private String sex;
	
	public static void main(String[] args) {
		NoArgsConstructorExample nace1 = new NoArgsConstructorExample();
		System.out.println(nace1.getAge());
		NoArgsConstructorExample nace2 = NoArgsConstructorExample.newInstance();
		System.out.println(nace2.getAge());
	}
}

@RequiredArgsConstructor:會生成一個包含常量,和標識了NotNull的變量 的構造方法。

@RequiredArgsConstructor(staticName="newInstance")
public class RequiredArgsConstructorExample {

	private final String name;
	
	@NonNull
	@Getter
	private String age;
	
	private String sex;
	
	public static void main(String[] args) {
		RequiredArgsConstructorExample race1 = new RequiredArgsConstructorExample("lisi", "18");
		System.out.println(race1.getAge());
		RequiredArgsConstructorExample race2 = RequiredArgsConstructorExample.newInstance("zhangsan", "16");
		System.out.println(race2.getAge());
	}
}

@AllArgsConstructor:會生成一個包含所有變量,同時如果變量使用了NotNull annotation , 會進行是否為空的校驗

@AllArgsConstructor(staticName="newInstance")
public class AllArgsConstructorExample {

	private final String name;
	
	@NonNull
	@Getter
	private String age;
	
	private String sex;
	public static void main(String[] args) {
		AllArgsConstructorExample aace1 = new AllArgsConstructorExample("zhangsan", "18", "female");
		System.out.println(aace1.getAge());
		AllArgsConstructorExample aace2 = AllArgsConstructorExample.newInstance("lisi", "16", "male");
		System.out.println(aace2.getAge());
	}
}

注意:三個注解生成的構造器都可以指定訪問權限,同時也可以提供一個靜態方法來供調用。三個注解的區別在於對final和@NonNull字段的處理不同

另外關於staticName屬性,Lombok源碼注釋如下:

If set, the generated constructor will be private, and an additional static 'constructor' is generated with the same argument list that wraps the real constructor.

很明顯三個注解都是可以使用構造器直接創建對象的,也可以使用靜態方法創建對象,不知道這段注釋是什么意思???

@Data注解

等同於@ToString, @EqualsAndHashcode, @Getter, @Setter和@RequiredArgsConstructor一起使用

@Value

@Value注解為不可變類型的@Data,是@Data的一個變種。它標注的類和字段都會被聲明為final

@Builder注解

@Builder注解為類生成builder api以供調用。Builder是一種解決包含數量巨大且繁雜的字段的類的一種構建方式。

假如一個類有幾十個字段,那么該如何設計這個類呢?

方法一:將幾十個字段都添加在構造函數中。簡單粗暴,而且在構造函數中為字段初始化也能夠保證對象能夠正確創建。缺點就是幾十個參數只會導致你在創建對象時記錯參數的位置,導致不必要的麻煩。

方法二:依賴注入。Spring的核心功能之一就是依賴注入,借助這種思想,我們通過無參構造創建一個對象,然后通過setter方法設置必需的屬性。這種方式可以根據需求初始化相關屬性,且邏輯清晰,但也會造成代碼繁瑣,需要調用多次setter方法。

方法三:Builder模式。建造者模式的思想就是將一個大的類的構建分為幾部分創建,從而簡化創建的復雜性。

@Builder
public class BuilderExample {
	private String name;
	private String age;
	private String sex;
	public static void main(String[] args) {
		BuilderExample be = BuilderExample.builder().name("zhangsan").age("16").sex("male").build();
		
		System.out.println(BuilderExample.builder().name("zhangsan").age("16").sex("male"));
	}
}

@Log

@Log注解為類添加一個日志對象log,類型為java.util.logging.Logger

這個類有很多變種,詳情如下:

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

@CleanUp注解

@CleanUp注解用於關閉資源,調用資源的close()方法

public class CleanUpExample {
	
	@SneakyThrows({FileNotFoundException.class, Exception.class})
	public static void main(String[] args) {
		File file = new File("C:/Users/wang2/Desktop/11.jpg");
		@Cleanup
		FileInputStream is = new FileInputStream(file);
		@Cleanup
		FileOutputStream os = new FileOutputStream(new File("C:/Users/wang2/Desktop/111.jpg"));
		
		byte[] buffer = new byte[1024];
		int length = 0;
		while((length = is.read(buffer)) != -1){
			os.write(buffer, 0, length);
		}
	}
}

注意:拋出的異常被@SneakyThrows捕獲了

@SneakyThrows注解

Sneaky的意思是偷偷摸摸地,@SneakyThrows注解的作用就是取代try...catch代碼塊,自動生成相應的try...catch代碼塊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM