華山論劍之JAVA三大代碼生成工具:Lombok、AutoValue和Immutables

Coding together

前言:開發(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的插件中心安裝

Find lombok plugin

2、Install Plugin

Install lombok 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)單携冤,如下所示:


IDEA 設(shè)置

更多關(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

AutoValue 注解處理

Immutables's Generation via Annotation Processing

Immutables 注解處理

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

Lombok注解處理

與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使用與原理

Immutables

AutoValue

Lombok, AutoValue, and Immutables

祝工作順利腮出,天天開心帖鸦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胚嘲,隨后出現(xiàn)的幾起案子作儿,更是在濱河造成了極大的恐慌,老刑警劉巖馋劈,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攻锰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡妓雾,警方通過(guò)查閱死者的電腦和手機(jī)娶吞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)械姻,“玉大人妒蛇,你說(shuō)我怎么就攤上這事】” “怎么了材部?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)唯竹。 經(jīng)常有香客問(wèn)我乐导,道長(zhǎng),這世上最難降的妖魔是什么浸颓? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任物臂,我火速辦了婚禮旺拉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘棵磷。我一直安慰自己蛾狗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布仪媒。 她就那樣靜靜地躺著沉桌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪算吩。 梳的紋絲不亂的頭發(fā)上留凭,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音偎巢,去河邊找鬼蔼夜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛压昼,可吹牛的內(nèi)容都是我干的求冷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼窍霞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼匠题!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起但金,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤韭山,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后傲绣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掠哥,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巩踏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年秃诵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片塞琼。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菠净,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出彪杉,到底是詐尸還是另有隱情毅往,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布派近,位于F島的核電站攀唯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏渴丸。R本人自食惡果不足惜侯嘀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一另凌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧戒幔,春花似錦吠谢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至敢订,卻和暖如春王污,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枢析。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工玉掸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人醒叁。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓司浪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親把沼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啊易,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 在面向?qū)ο缶幊讨斜夭豢缮傩枰诖a中定義對(duì)象模型,而在基于Java的業(yè)務(wù)平臺(tái)開發(fā)實(shí)踐中尤其如此饮睬。相信大家在平時(shí)開發(fā)...
    消失er閱讀 12,707評(píng)論 15 150
  • 轉(zhuǎn)自:juejin.im/post/5b00517cf265da0ba0636d4b 還在編寫無(wú)聊枯燥又難以維護(hù)的...
    GoJava閱讀 571評(píng)論 0 1
  • 每天租谈,每天 當(dāng)我枕著你的名字 從一夜幽夢(mèng)里悄然醒轉(zhuǎn) 我盼望著 可以斂清風(fēng)為筆 蘸星子為墨 為你寫盡這漫漫長(zhǎng)夜的思念...
    99太陽(yáng)風(fēng)閱讀 224評(píng)論 0 2
  • 我們?cè)谶M(jìn)行pod update 時(shí)有時(shí)對(duì)于某個(gè)pod的clone特別慢,導(dǎo)致我們創(chuàng)建private pod進(jìn)行l(wèi)i...
    nuannuan_nuan閱讀 3,360評(píng)論 0 0
  • 第四十五章 出路在哪里 安杰在痛苦的掙扎后做了正確的選擇——?jiǎng)h除了那條信息捆愁,他大大的低估了史小倩在文樂(lè)心...
    麗遙閱讀 376評(píng)論 0 0