前言:開發(fā)時(shí)棺耍,總是需要寫大量的Getter,Setter,hasCode等方法贡未,浪費(fèi)我們的時(shí)間,影響代碼的整潔蒙袍,干擾我們理解代碼的核心邏輯俊卤,對(duì)于這些煩不勝煩卻不得不寫的代碼,是時(shí)候交給自動(dòng)生成代碼工具去完成了害幅!當(dāng)前java語(yǔ)言最流行的自動(dòng)代碼生成工具當(dāng)屬Lombok消恍、AutoValue和Immutables了,然三者各有千秋以现,因此狠怨,了解并會(huì)使用這三款工具對(duì)于開發(fā)者很有必要。本文將詳細(xì)對(duì)比介紹這三款工具的使用方式邑遏,工具特點(diǎn)佣赖,適用場(chǎng)景,以及內(nèi)部原理记盒。
背景
實(shí)際開發(fā)中憎蛤,一般我們都會(huì)定義一些dto(網(wǎng)絡(luò)協(xié)議交互)和bean(數(shù)據(jù)庫(kù)交互協(xié)議)等數(shù)據(jù)結(jié)構(gòu)。這些類一般都需要有構(gòu)造函數(shù)孽鸡,字段的Getter蹂午,Setter方法,以及equals,hasCode方法和toString等方法。舉個(gè)例子彬碱,假如我們需要一個(gè)包含姓名和年齡兩個(gè)字段的Person類豆胸,那么他的代碼可能如下面所示:
public final class Person{
private final String name;
private final int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String name(){
return name;
}
public int age(){
return age;
}
@Override
public boolean equals(@Nullable Object o){
if(o instanceof Person){
Person person = (Person) o;
return name.equals(person.name) && age == person.age;
}
return false;
}
@Overrride
public int hashCode(){
return Objects.hashCode(name,age);
}
@Override
public String toString(){
return Objects.toStringHelper(this)
.add("name",name)
.add("age",age)
.tostring();
}
}
可以發(fā)現(xiàn):
1)簡(jiǎn)簡(jiǎn)單單的一個(gè)類代碼卻很長(zhǎng),或許你覺(jué)的可以不寫equals巷疼、hashCode和toString來(lái)減少代碼長(zhǎng)度晚胡,但是就會(huì)有如下問(wèn)題:
- 類對(duì)象不能放入HashSet中;
- 類對(duì)象不能作為一個(gè)key放入HashMap, Cache等等嚼沿;
- 類對(duì)象不能放入任何collection估盘,也不能進(jìn)行contains判斷;
- 類對(duì)象之間無(wú)法完成比較(比如測(cè)試時(shí)需要比較)骡尽;
- 如果不寫toString遣妥,打日志和debug時(shí)不方便。
2) 寫這些代碼勞神費(fèi)時(shí)攀细,還容易出錯(cuò)箫踩;
3) 非核心代碼太多爱态,影響用戶理解核心邏輯;
然而境钟,仔細(xì)觀察上面的類的方法锦担,可以發(fā)現(xiàn),其實(shí)對(duì)于所有的dto慨削,bean都是一樣的洞渔,都需要這些方法。那么其實(shí)可以抽一個(gè)模版出來(lái)缚态,自動(dòng)生成這些代碼磁椒,因此,自動(dòng)生成代碼工具誕生了猿规!當(dāng)前用的比較多的三種工具就是Lombok衷快、AutoValue和Immutables,本文將詳細(xì)對(duì)比分析這三款工具姨俩。
Lombok介紹
Lombok官方原版介紹如下:
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
Lombok使用
使用Lombok,首先是給IDEA安裝插件蘸拔,然后在使用時(shí)添加Lombok maven依賴,在相應(yīng)的類上加上Lombok注解即可环葵。
Lombok環(huán)境安裝
1调窍、通過(guò)IDEA的插件中心安裝
2、Install Plugin
有可能因?yàn)榫W(wǎng)絡(luò)原因张遭,插件安裝不上邓萨,那么可以選擇本地安裝:
1)首先下載Lombok插件包:從官方插件倉(cāng)庫(kù) 或者 Github Release 下載均可(注意Lombok版本與IDEA版本要對(duì)應(yīng));
2)進(jìn)入IDEA-->Settings/Preferences-->Plugins菊卷,在Plugins面板中有'install from disk'按鈕缔恳,點(diǎn)擊選擇剛下載的Lombok插件即可;
3)安裝成功洁闰。
為什么要安裝Lombok插件歉甚?
答:舉個(gè)例子,現(xiàn)在有一個(gè)A類扑眉,其中有一些字段纸泄,沒(méi)有創(chuàng)建它們的setter和getter方法,使用了lombok的注解生成腰素,另外有一個(gè)B類聘裁,它調(diào)用了A類實(shí)例的相應(yīng)字段的setter和getter方法。編譯A類弓千,并不會(huì)報(bào)錯(cuò)衡便,因?yàn)樽罱K生成的A類字節(jié)碼文件中存在相應(yīng)字段的setter和getter方法。但是,編譯B卻不行镣陕,IDE發(fā)現(xiàn)B類源代碼中所使用的A類實(shí)例的setter和getter方法在A類源代碼中找不到定義征唬,IDE會(huì)認(rèn)為這是錯(cuò)誤要解決。此時(shí)就需要下載安裝Intellij Idea中的"Lombok plugin"茁彭,可以幫助B類找到A類自動(dòng)生成的代碼(Class文件中)。Lombok詳細(xì)原理后面會(huì)介紹扶歪。
3理肺、Maven添加依賴
插件安裝成功后,項(xiàng)目使用Lombok時(shí)善镰,還需要添加Lombok macen依賴:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
經(jīng)過(guò)以上三步妹萨,就可以開始使用Lombok了双絮!
Lombok使用Demo
我們?nèi)匀幌胍厦娴拇a效果套才,使用Lombok的方式如下:
@Data
AllArgsConstructor
public final class Person{
private final String name;
private final int age;
}
生成的代碼(Class文件)如下:
public class Person {
private String naem;
private int age;
public Person() {
}
public String getNaem() {
return this.naem;
}
public int getAge() {
return this.age;
}
public void setNaem(String naem) {
this.naem = naem;
}
public void setAge(int age) {
this.age = age;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Person)) {
return false;
} else {
Person other = (Person)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$naem = this.getNaem();
Object other$naem = other.getNaem();
if (this$naem == null) {
if (other$naem == null) {
return this.getAge() == other.getAge();
}
} else if (this$naem.equals(other$naem)) {
return this.getAge() == other.getAge();
}
return false;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Person;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $naem = this.getNaem();
int result = result * 59 + ($naem == null ? 43 : $naem.hashCode());
result = result * 59 + this.getAge();
return result;
}
public String toString() {
return "Person(naem=" + this.getNaem() + ", age=" + this.getAge() + ")";
}
}
可以發(fā)現(xiàn)是在原class文件上進(jìn)行了增強(qiáng),添加了其他生成的代碼亡容。Data注解會(huì)生成無(wú)參構(gòu)造函數(shù)品洛,以及Getter树姨,Setter,equals桥状、hasCode和toString方法(Class文件中)帽揪。Lombok還包含很多其他注解可以使用,詳細(xì)說(shuō)明如下表格所示辅斟。
Lombok更多使用說(shuō)明
Lombok使用比較方便簡(jiǎn)單转晰,只需要添加相應(yīng)的注解即可,lombok提供的注解有:
注解 | 描述 |
---|---|
@Data | 注解在類上士飒,將類提供的所有屬性都添加查邢,包括get、set酵幕、equals扰藕、hashCode、toString方法 |
@NoArgsConstructor | 注解在類上裙盾,為類創(chuàng)建一個(gè)無(wú)參構(gòu)造函數(shù) |
@AllArgsConstructor | 注解在類上实胸,為類創(chuàng)建一個(gè)全參構(gòu)造函數(shù) |
@Setter | 注解在類上,為類所有屬性添加set方法番官;注解在屬性上庐完,為該屬性提供set方法 |
@Getter | 注解在類上,為所有的屬性添加get方法徘熔;注解在屬性上门躯,為該屬性提供get方法 |
@ToString | 注解在類上,為類提供一個(gè)toString方法 |
@NotNull | 在參數(shù)中使用時(shí)酷师,如果調(diào)用時(shí)傳了null值讶凉,就會(huì)拋出空指針異常 |
@Synchronized | 用于方法染乌,可以鎖定指定的對(duì)象,如果不指定懂讯,則默認(rèn)創(chuàng)建一個(gè)對(duì)象鎖定 |
@Log | 作用于類荷憋,創(chuàng)建一個(gè)log屬性 |
@Builder | 使用builder模式創(chuàng)建對(duì)象 |
@Accessors(chain = true) | 使用鏈?zhǔn)皆O(shè)置屬性,set方法返回的是this對(duì)象 |
@RequiredArgsConstructor | 創(chuàng)建對(duì)象, 例: 在class上添加 |
@RequiredArgsConstructor(staticName = "of") | 會(huì)創(chuàng)建生成一個(gè)靜態(tài)方法 |
@UtilityClass | 工具類 |
@ExtensionMethod | 設(shè)置父類 |
@FieldDefaults | 設(shè)置屬性的使用范圍褐望,如private勒庄、public等,也可以設(shè)置屬性是否被final修飾 |
@Cleanup | 關(guān)閉流瘫里、連接點(diǎn) |
@EqualsAndHashCode | 重寫equals和hashcode方法 |
@toString | 創(chuàng)建toString方法 |
@Cleanup | 用于流等可以不需要關(guān)閉使用流對(duì)象 |
AutoValue介紹
AutoValue是Google開源的一個(gè)Java源代碼生成器实蔽,用于為值對(duì)象或值類型對(duì)象生成源代碼。官方原版介紹如下:
AutoValue provides an easier way to create immutable value classes, with a lot less code and less room for error, while not restricting your freedom to code almost any aspect of your class exactly the way you want it.
AutoValue使用
使用AutoValue谨读,在項(xiàng)目中添加相應(yīng)的Maven依賴即可局装,詳細(xì)使用方法如下:
- 寫一個(gè)抽象類;
- 抽象類添加AutoValue注解劳殖;
- 不定義字段铐尚,而是定義抽象的獲得字段的方法;
- 定義一個(gè)獲取類實(shí)例的方法哆姻,方法返回子類(Auto_開頭)實(shí)例塑径;
- Javac編譯一下當(dāng)前類就會(huì)生成一個(gè)Auto_開頭的子類(子類包含父類的字段,構(gòu)造函數(shù)填具,Getter,toString,equals,hasCode)统舀;
AutoValue環(huán)境依賴
AutoValue不需要安裝插件,只需要在使用時(shí)添加jar包即可劳景,maven依賴如下:
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<version>1.6.6</version>
<scope>provided</scope>
</dependency>
AutoValue使用Demo
我們?nèi)匀灰訮erson類作為示例誉简,那么此時(shí)Person就如下面所示:
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Person {
public static Person create(String name, int age) {
return new AutoValue_Person(name, age);
}
public abstract String name();
public abstract int age();
}
編譯一下上面的代碼,就會(huì)生成Person的子類Auto_Person盟广,類代碼(.java)如下:
import javax.annotation.Generated;
@Generated("com.google.auto.value.processor.AutoValueProcessor")
final class AutoValue_Person extends Person {
private final String name;
private final int age;
AutoValue_Person(String name,int age) {
if (name == null) {
throw new NullPointerException("Null name");
}
this.name = name;
this.age = age;
}
@Override
public String name() {
return name;
}
@Override
public int age() {
return age;
}
@Override
public String toString() {
return "Person{"
+ "name=" + name + ", "
+ "age=" + age
+ "}";
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof Person) {
Person that = (Person) o;
return this.name.equals(that.name())
&& this.age == that.age();
}
return false;
}
@Override
public int hashCode() {
int h$ = 1;
h$ *= 1000003;
h$ ^= name.hashCode();
h$ *= 1000003;
h$ ^= age;
return h$;
}
}
AutoValue與Lombok不同闷串,它生成的是Java源文件,因此也不需要添加其他插件了筋量,其他類想要用此類的Getter法烹吵,直接用即可。
Immutables介紹
Immutables官方介紹:
Java annotation processors to generate simple, safe and consistent value objects. Do not repeat yourself, try Immutables, the most comprehensive tool in this field!
Immutables使用
使用Immutables桨武,首先需要簡(jiǎn)單的設(shè)置一下IDE環(huán)境肋拔,然后項(xiàng)目中添加相應(yīng)的Maven依賴,接下來(lái)就可以使用Immutables了呀酸,使用方法與AutoValue類似凉蜂,如下:
- 寫一個(gè)抽象類或接口;
- 抽象類添加Immutables的注解;
- 不定義字段窿吩,而是定義抽象的獲得字段的方法茎杂;
- Javac編譯一下當(dāng)前類就會(huì)生成一個(gè)子類(一般均是Immutable開頭,子類提供構(gòu)父類的方法纫雁,以及getter,toString,equals煌往,hasCode等模版方法);
Immutables環(huán)境依賴
1轧邪、設(shè)置IDE
IDEA的設(shè)置比較簡(jiǎn)單携冤,如下所示:
更多關(guān)于Eclipse以及IDEA配置,請(qǐng)看官方文檔:Using annotation processor in IDE
2闲勺、Maven添加依賴
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.7.4</version>
<scope>provided</scope>
</dependency>
Immutables使用Demo
@Value.Immutable
public interface TypicalUseDemo {
String name();
int age();
}
然后就會(huì)生成如下代碼(.java):
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.immutables.value.Generated;
@Generated(
from = "TypicalUseDemo",
generator = "Immutables"
)
public final class ImmutableTypicalUseDemo implements TypicalUseDemo {
private final String name;
private final int age;
private ImmutableTypicalUseDemo(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return this.name;
}
public int age() {
return this.age;
}
public final ImmutableTypicalUseDemo withName(String value) {
String newValue = (String)Objects.requireNonNull(value, "name");
return this.name.equals(newValue) ? this : new ImmutableTypicalUseDemo(newValue, this.age);
}
public final ImmutableTypicalUseDemo withAge(int value) {
return this.age == value ? this : new ImmutableTypicalUseDemo(this.name, value);
}
public boolean equals(Object another) {
if (this == another) {
return true;
} else {
return another instanceof ImmutableTypicalUseDemo && this.equalTo((ImmutableTypicalUseDemo)another);
}
}
private boolean equalTo(ImmutableTypicalUseDemo another) {
return this.name.equals(another.name) && this.age == another.age;
}
public int hashCode() {
int h = 5381;
int h = h + (h << 5) + this.name.hashCode();
h += (h << 5) + this.age;
return h;
}
public String toString() {
return "TypicalUseDemo{name=" + this.name + ", age=" + this.age + "}";
}
public static ImmutableTypicalUseDemo copyOf(TypicalUseDemo instance) {
return instance instanceof ImmutableTypicalUseDemo ? (ImmutableTypicalUseDemo)instance : builder().from(instance).build();
}
public static ImmutableTypicalUseDemo.Builder builder() {
return new ImmutableTypicalUseDemo.Builder();
}
@Generated(
from = "TypicalUseDemo",
generator = "Immutables"
)
public static final class Builder {
private static final long INIT_BIT_NAME = 1L;
private static final long INIT_BIT_AGE = 2L;
private long initBits;
private String name;
private int age;
private Builder() {
this.initBits = 3L;
}
public final ImmutableTypicalUseDemo.Builder from(TypicalUseDemo instance) {
Objects.requireNonNull(instance, "instance");
this.name(instance.name());
this.age(instance.age());
return this;
}
public final ImmutableTypicalUseDemo.Builder name(String name) {
this.name = (String)Objects.requireNonNull(name, "name");
this.initBits &= -2L;
return this;
}
public final ImmutableTypicalUseDemo.Builder age(int age) {
this.age = age;
this.initBits &= -3L;
return this;
}
public ImmutableTypicalUseDemo build() {
if (this.initBits != 0L) {
throw new IllegalStateException(this.formatRequiredAttributesMessage());
} else {
return new ImmutableTypicalUseDemo(this.name, this.age);
}
}
private String formatRequiredAttributesMessage() {
List<String> attributes = new ArrayList();
if ((this.initBits & 1L) != 0L) {
attributes.add("name");
}
if ((this.initBits & 2L) != 0L) {
attributes.add("age");
}
return "Cannot build TypicalUseDemo, some of required attributes are not set " + attributes;
}
}
}
Immutables更多使用說(shuō)明
Immutables支持的特性較多,很靈活扣猫,包括:
設(shè)置可見范圍
設(shè)置可見范圍注解如下:
@Value.Style(visibility = Value.Style.ImplementationVisibility.PRIVATE)
可見范圍可以設(shè)置為:PUBLIC菜循,SAME,SAME_NON_RETURNED申尤,PACKAGE癌幕,PRIVATE,不同的可見范圍,ImmutableVisibilityDemo的位置以及可見范圍不同昧穿,如下代碼為可見范圍為PRIVATE的Demo:
@Value.Immutable
@Value.Style(visibility = Value.Style.ImplementationVisibility.PRIVATE)
public interface VisibilityDemo {
String name();
int age();
}
//test
VisibilityDemo demo = new VisibilityDemoBuilder().name("pioneeryi").age(26).build();
根據(jù)構(gòu)造函數(shù)構(gòu)造
作為構(gòu)造對(duì)象一種方式勺远,Immutable實(shí)現(xiàn)類將提供一個(gè)靜態(tài)的方法of用來(lái)構(gòu)造對(duì)象。此時(shí)類的每個(gè)屬性需要加上如下注解:
@Value.Parameter
類的屬性都加上Value.Parameter的注解后时鸵,我們?cè)跇?gòu)造對(duì)象即可不采用Builder模式胶逢,而采用構(gòu)造函數(shù)模式,同時(shí)我們還可以關(guān)閉Builder模式饰潜,此時(shí)生成的代碼中就不會(huì)Builder模式代碼了初坠,僅有構(gòu)造函數(shù)一種構(gòu)造對(duì)象方法,使用Demo如下:
@Value.Immutable(builder = false, copy = false)
public interface ConstructorDemo {
@Value.Parameter String name();
@Value.Parameter
int age();
}
//test
ConstructorDemo demo = ImmutableConstructorDemo.of("pioneeryi", 26);
設(shè)置默認(rèn)值
我們希望給類對(duì)象屬性彭雾,提供默認(rèn)值碟刺,此時(shí)可以使用如下注解,注解相應(yīng)屬性:
@Value.Default
使用此注解后薯酝,那么如果構(gòu)造時(shí)沒(méi)有設(shè)置屬性值即使用默認(rèn)值半沽,使用Demo如下:
@Value.Immutable
public abstract class DefaultAttributesDemo {
public abstract String name();
@Value.Default
public int age() {
return 26;
}
}
//test
DefaultAttributesDemo demo = ImmutableDefaultAttributesDemo.builder().name("pioneeryi").build();
String expect = "DefaultAttributesDemo{name=pioneeryi, age=26}";
Assert.assertTrue(demo.toString().equals(expect));
DefaultAttributesDemo demo = ImmutableDefaultAttributesDemo.builder().name("pioneeryi").age(18).build();
String expect = "DefaultAttributesDemo{name=pioneeryi, age=18}";
Assert.assertTrue(demo.toString().equals(expect));
參數(shù)校驗(yàn)
如果需要校驗(yàn)屬性的值是否合法,可以使用如下注解:
@Value.Check
詳細(xì)使用Demo如下:
@Value.Immutable
public abstract class PreconditionDemo {
public abstract String name();
public abstract int age();
@Value.Check
protected void check() {
Preconditions.checkArgument(name() != null && !name().equals(""), "name can not be empty");
Preconditions.checkArgument(age() > 0, "age is not valid");
}
}
變成可變類
如果希望將類由不可變類變成可變類吴菠,可以使用如下注解:
@Value.Modifiable
使用此注解后者填,會(huì)生成兩個(gè)子實(shí)現(xiàn)類,一個(gè)時(shí)不可變的做葵,另一個(gè)是可變的幔托,Demo如下:
@Value.Immutable
@Value.Modifiable
public interface ModifiableDemo {
String getName();
int getAge();
}
//test immutable
ModifiableDemo demo = ImmutableModifiableDemo.builder().name("pioneeryi").age(26).build();
String expect = "ModifiableDemo{name=pioneeryi, age=26}";
Assert.assertTrue(demo.toString().equals(expect));
//test modifiable
ModifiableDemo demo = ModifiableModifiableDemo.create();
((ModifiableModifiableDemo) demo).setName("pioneeryi");
((ModifiableModifiableDemo) demo).setAge(26);
String expect = "ModifiableModifiableDemo{name=pioneeryi, age=26}";
Assert.assertTrue(demo.toString().equals(expect));
更多Immutables的特性和使用技巧,請(qǐng)看官方介紹文檔:Immutable objects
對(duì)比分析
值對(duì)象
Lombok、AutoValue和immutable都支持“值對(duì)象”的生成重挑。
AutoValue專注于生成值對(duì)象嗓化,并支持基于模板類中的抽象方法生成字段、構(gòu)造函數(shù)/構(gòu)建器谬哀、具體的訪問(wèn)器方法刺覆,以及公共方法equals(Object)、hashCode()和toString()的實(shí)現(xiàn)史煎;
Immutables提供了與AutoValue類似的功能谦屑,并添加了使用@value.modiizable生成可修改類的功能,同時(shí)還提供了其他功能篇梭,見Immutable更多使用說(shuō)明氢橙;
Lombok提供了與使用@Value注釋的AutoValue類似的值類生成功能,并提供了使用@Data注釋生成可修改類的功能恬偷,同時(shí)還提供了其他功能悍手,見Lombok更多使用說(shuō)明。
原理
三者原理都是基于“Annotation Processing“袍患,每個(gè)工具都在他們的JAR文件的“META-INF/services”中定義了一個(gè)javax.annotation.processing.Processor坦康,最終完成了“從一個(gè)簡(jiǎn)潔的模板類生成包含更多方法(Getter,toString,hasCode诡延,equals等)的生成類”的目的滞欠。
盡管Lombok、AutoValue和Immutables都使用了javac的annotation processing肆良,但是Lombok使用注釋處理的方式與AutoValue和Immutables使用注釋處理的方式不同筛璧。
AutoValue和Immutables在更傳統(tǒng)的意義上使用注釋處理,并從源代碼生成源代碼惹恃。由AutoValue和Immutables生成的類源代碼的名稱與模板類不同隧哮,實(shí)際上擴(kuò)展了模板類,它有自己的名稱座舍,包含所有生成的方法和字段沮翔,這避免了與模板類的任何名稱沖突,并且使在同一個(gè)IDE項(xiàng)目中混合模板類源代碼和生成的類源代碼變得相當(dāng)容易曲秉,因?yàn)樗鼈儗?shí)際上是不同的類采蚀。
AutoValue's Generation via Annotation Processing
Immutables's Generation via Annotation Processing
Lombok生成一個(gè)與“模板”源代碼具有相同類名的編譯后的.class文件,并將生成的方法添加到這個(gè)編譯后的版本中承二。開發(fā)人員只在查看.java文件時(shí)看到簡(jiǎn)潔的模板代碼榆鼠,而在查看.class文件時(shí)看到編譯后的.class文件,其中的方法在源代碼中不存在亥鸠。Lombok生成的不是另一個(gè)源文件妆够,而是原始源文件的增強(qiáng)編譯版本识啦。有一個(gè)delombok選項(xiàng)可以與Lombok一起使用,查看增強(qiáng).class文件后面生成的源代碼是什么樣子的神妹,但是該項(xiàng)目的設(shè)計(jì)目的是直接從簡(jiǎn)潔的模板源代碼轉(zhuǎn)換為增強(qiáng)的編譯類颓哮,而不需要或不使用中間增強(qiáng)的源文件。delombok選項(xiàng)可以用來(lái)看看生成的源樣子或,或許更重要的是,可以使用的工具是混亂的情況下不一致的源(簡(jiǎn)潔的模板. java文件)和生成的類(增強(qiáng)的. class文件名稱相同)在同一個(gè)空間鸵荠。
Lombok's Generation via Annotation Processing
與AutoValue和Immutables使用的方法相比冕茅,Lombok的注釋處理方法不那么傳統(tǒng),包括Lombok的創(chuàng)建者在內(nèi)的一些人將這種方法稱為“黑客”蛹找,因?yàn)樗褂梅菢?biāo)準(zhǔn)api姨伤,因此很難與ide和其他執(zhí)行自己編譯的工具(如javadoc)很好地集成。但是因?yàn)锳utoValue和Immutables會(huì)生成帶有新類名的源代碼庸疾,所以任何傳統(tǒng)工具和ide都可以使用生成的源代碼和模板源代碼一起工作乍楚,而不會(huì)有任何重大問(wèn)題。
三者詳細(xì)差異對(duì)比
對(duì)比項(xiàng) | Lombok | AautoValue | Immutables |
---|---|---|---|
License | MIT (also) | Apache 2 | Apache 2 |
最低java版本 | 1.6 | 1.6 | 1.7 |
生成的文件 | lombok修改了原class文件届慈,加入生成的代碼 | 生成了另外一個(gè)java子類徒溪,不侵入原有的java代碼,完全遵循java的規(guī)范,可以看到兩個(gè)java文件和兩個(gè)class文件 | 生成了另外一個(gè)java子類,不侵入原有的java代碼,完全遵循java的規(guī)范,可以看到兩個(gè)java文件和兩個(gè)class文件 |
生成類與模版類關(guān)系 | Enhanced generated class replaces template source | Generated source extends template source | Generated source extends template source |
查看生成類 | 使用delombok | 默認(rèn)可見 | 默認(rèn)可見 |
使用方便性 | 為類或字段添加注解即可 | 加上注解的同時(shí)拧篮,需要按照一定的規(guī)范遍寫代碼 | 加上注解的同時(shí),需要按照一定的規(guī)范遍寫代碼 |
是否需要提前編譯 | 不用牵舱,加上注解后串绩,就可以用其生成的方法 | 編譯一次,才能生效芜壁,編譯前是找不到待生成的子類的 | 編譯一次礁凡,才能生效,編譯前是找不到待生成的子類的 |
生成的代碼是否可見 | 不可見慧妄,實(shí)在要看需要反編譯顷牌,不利于Debug可代碼分析比如覆蓋率等 | 可以看見生成的源代碼,在代碼調(diào)試和分析時(shí)較方便 | 可以看見生成的源代碼塞淹,在代碼調(diào)試和分析時(shí)較方便 |
不可變程度 | 可以使用set方法修改類 | 可以使用Immutability修改類 | 強(qiáng)支持不可變 |
如何選擇這三個(gè)工具
1)AutoValue和Immutables使用標(biāo)準(zhǔn)注釋處理窟蓝,Lombok使用非標(biāo)準(zhǔn)注釋處理方法:
- 開發(fā)者如果希望避免非標(biāo)準(zhǔn)依賴,那么應(yīng)該使用AutoValue和Immutables饱普;
- 開發(fā)者不希望添加IDE插件或者其他非javac以及非基礎(chǔ)Java IDE支持的第三方工具运挫,那么建議使用AutoValue和Immutables;
2)Lombok修改了原class文件套耕,生成的類與模版類在同一個(gè)包下谁帕,并且名字相同;AutoValue和Immutables生成的類繼承自基礎(chǔ)模版類冯袍,但是在同一個(gè)包下:
- 開發(fā)者如果希望編譯的class文件和源文件在同一個(gè)包下匈挖,并且同名碾牌,那么應(yīng)該使用Lombok;
- 開發(fā)者如果希望可以看到生成的代碼儡循,并且不希望影響原來(lái)的代碼舶吗,那么應(yīng)該使用AutoValue和immutebles;
3)三個(gè)工具都不同程度上的支持自定義,因此和這個(gè)需要根據(jù)實(shí)際需要進(jìn)行選擇:
Lombok提供了一個(gè)configuration system 贮折,允許根據(jù)所需的約定調(diào)整生成代碼裤翩。
Immutables提供了style customization,允許對(duì)生成的代碼的多個(gè)點(diǎn)進(jìn)行調(diào)整调榄。
AutoValue允許用戶通過(guò)一些方式User Guide踊赠,自行定義一些生成代碼的規(guī)則。
4)從可變性看每庆,三者的opinionated不同筐带,AutoValue是不支持可變的,而Lombok和Immutables支持:
- 希望類成為不可變類缤灵,使用AutoValue;
- 希望類一定程度上支持可變伦籍,那么使用Lombk或者Immutables;
參考文檔
Lombok, AutoValue, and Immutables
祝工作順利腮出,天天開心帖鸦!