Jilt使い方メモ

# Jiltとは
- ビルダークラスをアノテーションプロセッサーで自動生成する Java ライブラリ
- 単純にソースコードを生成する仕組みなので、実行時に依存関係は不要となり、バイトコードをいじるといった黒魔術も使わない
- 必須プロパティを強制できる形式のビルダークラス(Staged Builder)を生成できる

# 環境
IDE: IntelliJ IDEA 2024.1.3 (Community Edition)

```text:Gradle
> gradle --version

------------------------------------------------------------
Gradle 8.8
------------------------------------------------------------

Build time:   2024-05-31 21:46:56 UTC
Revision:     4bd1b3d3fc3f31db5a26eecb416a165b8cc36082

Kotlin:       1.9.22
Groovy:       3.0.21
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          21.0.3 (Eclipse Adoptium 21.0.3+9-LTS)
OS:           Windows 10 10.0 amd64
```

```text:Maven
> mvn --version
Apache Maven 3.9.7 (8b094c9513efc1b9ce2d952b3b9c8eaedaf8cbf0)
Maven home: C:\pf\maven\latest
Java version: 21.0.3, vendor: Eclipse Adoptium, runtime: C:\pf\java\latest
Default locale: ja_JP, platform encoding: UTF-8
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
```

# Hello World
## IntelliJ の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28302/fa8bece1-2ccc-cc37-b979-738eba4789ea.png)

- File > Settings を開き、 Build, Execution, Deployment > Compiler > Annotation Processors の Enable annotation processing のチェックを**オン**にする


## 実装
```gradle:build.gradle
plugins {
    id "java"
}

java {
    sourceCompatibility = 21
    targetCompatibility = 21
}

compileJava.options.encoding = "UTF-8"

repositories {
    mavenCentral()
}

dependencies {
    compileOnly "cc.jilt:jilt:1.6"
    annotationProcessor "cc.jilt:jilt:1.6"
}
```


::: note info
**Maven の場合**

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>example</groupId>
    <artifactId>jilt</artifactId>
    <version>1.0.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>cc.jilt</groupId>
            <artifactId>jilt</artifactId>
            <version>1.6</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
```

依存関係を `provided` で設定する。
:::


```java:Hoge.java
package sandbox.jilt;

import org.jilt.Builder;

@Builder
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:Main.java
package sandbox.jilt;

public class Main {

    public static void main(String[] args) {
        final Hoge hoge = HogeBuilder.hoge().text("Hello Jilt!!").number(9).build();
        System.out.println(hoge);
    }
}
```

```text:実行結果
Hoge{text='Hello Jilt!!', number=9}
```


## 説明
```gradle:build.gradle
dependencies {
    compileOnly "cc.jilt:jilt:1.6"
    annotationProcessor "cc.jilt:jilt:1.6"
}
```

- `compileOnly` と `annotationProcessor` に [cc.jilt:jilt](https://central.sonatype.com/artifact/cc.jilt/jilt/overview) を設定する

```java:Hoge.java
import org.jilt.Builder;

@Builder
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

- ビルダークラスを作りたいクラスに `@Builder` アノテーションを付ける
- フィールドの宣言順序と同じ順番で引数を受け取るコンストラクタを定義しておく

```java:HogeBuilder.java
package sandbox.jilt;

import java.lang.String;
import javax.annotation.processing.Generated;

@Generated("Jilt-1.6")
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- プロジェクトをビルドすると、 `build/generated/sources/annotationProcessor/java/main` の下に `HogeBuilder.java` が出力される
- Maven の場合は `target/generated-sources/annotations` の下に出力される

```java:Main.java
        final Hoge hoge = HogeBuilder.hoge().text("Hello Jilt!!").number(9).build();
```

- 出力された `HogeBuilder` を使って `Hoge` のインスタンス生成を書くことができる

# Builder
## クラスに `@Builder` を付ける
```java:Hoge.java
import org.jilt.Builder;

@Builder
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- クラスに `@Builder` アノテーションを付けると、そのクラスに宣言されている全てのフィールドを受け取れるビルダーが生成される
- `build` メソッドの中でビルダーが呼び出すコンストラクタは、フィールドの宣言と同じ順序で引数が並んでいることを想定している
    - ここでは `String text` > `int number` の順でフィールドが定義されているので、コンストラクタ引数も同じ順序で並んでいることが期待される
    - 順序が異なるとコンパイルエラーになったり、型が同じだけど異なる値が渡されてしまう恐れがある

### 対象外のフィールドを指定する
```java:Hoge.java
import org.jilt.Builder;

@Builder
public class Hoge {
    private final String text;
    private final int number;
    @Builder.Ignore
    private boolean bool;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- `@Builder.Ignore` アノテーションをフィールドにつけると、そのフィールドは除外される

## コンストラクタに `@Builder` を付ける
```java:Hoge.java
import org.jilt.Builder;

public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }

    @Builder
    public Hoge(String text) {
        this(text, 999);
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public Hoge build() {
    return new Hoge(text);
  }
}
```

- 特定のコンストラクタに `@Builder` を設定することで `build` メソッドで呼び出すコンストラクタを指定できる
- この場合、ビルダーで設定できるプロパティはコンストラクタで渡しているモノだけに絞られる

## static メソッドに `@Builder` を付ける
```java:Hoge.java
import org.jilt.Builder;

public class Hoge {
    private final String text;
    private final int number;

    @Builder
    public static Hoge create(String text, int number) {
        return new Hoge(text, number);
    }

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
package sandbox.jilt;

...

public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return Hoge.create(text, number);
  }
}
```

- `static` メソッド(ファクトリメソッド)に `@Builder` を付けることができる
- この場合、ファクトリメソッドが `build` メソッドの中から呼ばれる形になる
- この方法は最も柔軟にインスタンスを生成できる手段で、ファクトリメソッドさえ用意すればライブラリが提供するクラスのビルダーを作ることもできるようになる


## ビルダーのクラス名を指定する
```java:Hoge.java
import org.jilt.Builder;

@Builder(className = "MyHogeFactory")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

- `className` で生成されるビルダークラスの名前を指定できる
- 未指定の場合のデフォルトは、生成対象のクラス名の後ろに `Builder` がついたものになる


## 出力先のパッケージを指定する
```java:Hoge.java
@Builder(packageName = "sandbox.jilt.builders")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
package sandbox.jilt.builders;

import java.lang.String;
import javax.annotation.processing.Generated;
import sandbox.jilt.Hoge;

@Generated("Jilt-1.6")
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- `@Builder` の `packageName` でビルダーの出力先パッケージを指定できる
- 未指定の場合のデフォルトは、生成対象のクラスと同じパッケージになる


## セッターメソッドのプレフィックスを指定する
```java:Hoge.java
@Builder(setterPrefix = "set")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder setText(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder setNumber(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- `setterPrefix` で、ビルダーのセッターメソッドのプレフィックスを指定できる
- デフォルトは空で、セッターメソッドはフィールド名と同じになる

## ビルダーのファクトリメソッド名を変更する
```java:Hoge.java
@Builder(factoryMethod = "newBuilder")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder newBuilder() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- `factoryMethod` で、ビルダーのインスタンスを最初に生成するためのファクトリメソッド名を変更できる
- 未指定の場合のデフォルトは、生成対象のクラス名の先頭を小文字にしたものになる

## ビルドメソッド名を変更する
```java:Hoge.java
@Builder(buildMethod = "create")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge create() {
    return new Hoge(text, number);
  }
}
```

- `buildMethod` で、最後にインスタンスを構築して返すビルドメソッドの名前を変更できる
- 未指定の場合のデフォルトは `build`

## 既存のインスタンスからビルダーを生成する
```java:Hoge.java
@Builder(toBuilder = "from")
public class Hoge {
    final String text;
    private final int number;
    private final boolean bool;

    public Hoge(String text, int number, boolean bool) {
        this.text = text;
        this.number = number;
        this.bool = bool;
    }

    public int number() {
        return number;
    }

    public int getNumber() {
        return number;
    }

    public boolean isBool() {
        return bool;
    }
}
```

```java:HogeBuilder.java
public class HogeBuilder {
  private String text;

  private int number;

  private boolean bool;

  public static HogeBuilder hoge() {
    return new HogeBuilder();
  }

  public static HogeBuilder from(Hoge hoge) {
    HogeBuilder hogeBuilder = new HogeBuilder();
    hogeBuilder.text(hoge.text);
    hogeBuilder.number(hoge.number());
    hogeBuilder.bool(hoge.isBool());
    return hogeBuilder;
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public HogeBuilder bool(final boolean bool) {
    this.bool = bool;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number, bool);
  }
}
```

```java
final Hoge hoge = HogeBuilder.hoge().text("a").number(10).bool(true).build();
final Hoge copy = HogeBuilder.from(hoge).number(90).build();
```

- `toBuilder` を設定すると、既存のインスタンスを元に新しいビルダーを生成するメソッドが追加される
    - ここに指定した値が、そのメソッドの名前になる
    - 未指定の場合、このメソッドは生成されない
- 生成されたメソッドは、受け取った既存のインスタンスから各フィールドの値を取得し、新しいビルダーに設定したうえでビルダーを返すようになっている
    - 既存のインスタンスから値を取得する方法は、以下の優先順序で行われる
        1. フィールドを直接参照して取得する
        1. フィールド名と同名の Getter メソッドを使用して取得する
        1. フィールド名に対応するプロパティの Getter メソッドを使用して取得する



::: note warn
**Staged Builder と共存できない**

この `toBuilder` と後述する Staged Builder は共存できない。
両方を同時に設定していると、 Staged Builder の方が動作しなくなる。

具体的には、 `STAGED` を指定してもビルダーのプロパティを設定するメソッドの戻り値がインタフェースにならず、ビルダークラスのままになる。
つまり、次に呼び出せるメソッドが限定されなくなるので、 Staged Builder としての振る舞いが失われた状態になる。

```java:STAGEDを指定して出力されたビルダーの一部
  // 戻り値の型がインタフェースではなくビルダーのままなので、
  // 次に呼び出すメソッドは自由になる
  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }
```

特にドキュメントでは触れられていないので、バグなのか仕様なのかは分からない。
（STAGED の挙動を有効にすると、「他のインスタンスを元にして一部だけ変更する」という `toBuilder` の目的が達成できないので仕様な気はする）
:::

# Staged Builder
```java:Hoge.java
package sandbox.jilt;

import org.jilt.Builder;
import org.jilt.BuilderStyle;

@Builder(style = BuilderStyle.STAGED)
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
package sandbox.jilt;

import java.lang.String;
import javax.annotation.processing.Generated;

@Generated("Jilt-1.6")
public interface HogeBuilders {
  interface Text {
    Number text(final String text);
  }

  interface Number {
    Optionals number(final int number);
  }

  interface Optionals {
    Hoge build();
  }
}
```

```java:HogeBuilder.java
package sandbox.jilt;

import java.lang.String;
import javax.annotation.processing.Generated;

@Generated("Jilt-1.6")
public class HogeBuilder implements HogeBuilders.Text,
                                    HogeBuilders.Number,
                                    HogeBuilders.Optionals {
  private String text;

  private int number;

  private HogeBuilder() {
  }

  public static HogeBuilders.Text hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

- `@Builder` の `style` に `BuilderStyle.STAGED` を設定することで、 Staged Builder を生成できる
- Staged Builder は、各プロパティを初期化するためのメソッドを呼び出せる順序が限定されたビルダーとなっている
    - 例えば上記例の場合、 `text` > `number` の順でしかプロパティの設定ができないようになっている
    - また、 `text` や `number` を設定せずに `build` を呼び出すこともできない

```java:以下は全てコンパイルエラーになる
HogeBuilder.hoge()
           .number(8) // text の前に number を設定することはできない
           .text("Hello Jilt!!")
           .build();
           
HogeBuilder.hoge()
           .text("Hello Jilt!!")
           .build(); // number の設定を省略できない
```

- この仕組みは、ビルダーの各メソッドが次に呼び出すべきメソッドだけを定義したインタフェースを返すことで実現されている

```plantuml
scale 0.8

interface "HogeBuilders.Text" as Text {
    text(text: String): HogeBuilders.Number
}

interface "HogeBuilders.Number" as Number {
    number(number: int): HogeBuilders.Optionals
}

interface "HogeBuilders.Optionals" as Optionals {
    build(): Hoge
}

class HogeBuilder implements Text, Number, Optionals {
    {static} hoge(): HogeBuilders.Text
}
```

- 起点となる `HogeBuilder.hoge` メソッドは、 `HogeBuilders.Text` を返す形になっている
- この `HogeBuilders.Text` は `text(String text)` という `text` プロパティを設定するメソッドだけが定義されている
- そして、戻り値の型が `HogeBuilders.Number` になっている
- `HogeBuilders.Number` には、 `number` を設定するための `number(int number)` メソッドだけが定義されている
- これにより、 `text` プロパティを設定した後は `number` の設定だけしかできないようになっている
- ビルダーのメソッドが返しているインスタンスは、いずれも同じ `HogeBuilder` のインスタンスだが、インタフェースによって呼び出せるメソッドを絞り込むことによって上手い具合に制御されている

## 任意プロパティを定義する
```java:Hoge.java
@Builder(style = BuilderStyle.STAGED)
public class Hoge {
    private final String text;
    @Opt
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
public interface HogeBuilders {
  interface Text {
    Optionals text(final String text);
  }

  interface Optionals {
    Optionals number(@Opt final int number);

    Hoge build();
  }
}
```

```java:HogeBuilder.java
public class HogeBuilder implements HogeBuilders.Text, HogeBuilders.Optionals {
  private String text;

  private int number;

  private HogeBuilder() {
  }

  public static HogeBuilders.Text hoge() {
    return new HogeBuilder();
  }

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(@Opt final int number) {
    this.number = number;
    return this;
  }

  public Hoge build() {
    return new Hoge(text, number);
  }
}
```

```plantuml
scale 0.8

interface "HogeBuilders.Text" as Text {
    text(text: String): HogeBuilders.Number
}

interface "HogeBuilders.Optionals" as Optionals {
    number(number: int): HogeBuilders.Optionals
    build(): Hoge
}

class HogeBuilder implements Text, Optionals {
    {static} hoge(): HogeBuilders.Text
}
```

- `@Opt` アノテーションを付けると、そのプロパティは任意（オプショナル）になる
- `HogeBuilders.Number` インタフェースが消えて、 `HogeBuilders.Optionals` に `number` メソッドが追加されている
- これにより、 `number` の設定は省略して書けるようになる
    - 未設定のプロパティは、各型の初期値が設定される
- 任意プロパティは、必須プロパティを設定し終えた後で `build` メソッドを呼び出す前に設定できる

```java
// number メソッドの呼び出しを省略できる
HogeBuilder.hoge().text("a").build();

// number は、必須の text を指定した後に設定できる
HogeBuilder.hoge().text("a").number(1).build();
```

- `@Builder` をコンストラクタや static メソッドに設定している場合、 `@Opt` はメソッドパラメータに設定する

```java
public class Hoge {
    private final String text;

    private final int number;

    @Builder(style = BuilderStyle.STAGED)
    public Hoge(String text, @Opt int number) {
        this.text = text;
        this.number = number;
    }
}
```

### Nullable が設定されているプロパティは任意扱いになる
```java:Nullable.java
package sandbox.jilt;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Nullable {
}
```

- 自前の `Nullable` アノテーション

```java:Hoge.java
package sandbox.jilt;

import org.jilt.Builder;
import org.jilt.BuilderStyle;

@Builder(style = BuilderStyle.STAGED)
public class Hoge {
    private final String text;
    @Nullable
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

- `number` に自作の `Nullable` を設定する

```java:HogeBuilders.java
public interface HogeBuilders {
  interface Text {
    Optionals text(final String text);
  }

  interface Optionals {
    Optionals number(@Nullable final int number);

    Hoge build();
  }
}
```

- `number` が `Optionals` に出力されている
- `Nullable` という名前のアノテーションがつけられている場合、そのプロパティは自動的に任意扱いになる
    - 名前だけで判定しているようなので、 JSR305 の `javax.annotation.Nullable` でも JetBrains annotation の `org.jetbrains.annotations.Nullable` でも適用される


### 順序を維持する
```java:Hoge.java
package sandbox.jilt;

import org.jilt.Builder;
import org.jilt.BuilderStyle;
import org.jilt.Opt;

@Builder(style = BuilderStyle.STAGED_PRESERVING_ORDER)
public class Hoge {
    @Opt
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
public interface HogeBuilders {
  interface Text {
    Number text(@Opt final String text);

    Build number(final int number);
  }

  interface Number {
    Build number(final int number);
  }

  interface Build {
    Hoge build();
  }
}
```

```plantuml
scale 0.8

interface "HogeBuilders.Text" as Text {
    text(text: String): HogeBuilders.Number
    number(number: int): HogeBuilders.Build
}

interface "HogeBuilders.Number" as Number {
    number(number: int): HogeBuilders.Build
}

interface "HogeBuilders.Build" as Build {
    build(): Hoge
}

class HogeBuilder implements Text, Number, Build {
    {static} hoge(): HogeBuilders.Text
}
```

- `@Builder` の `style` に `BuilderStyle.STAGED_PRESERVING_ORDER` を設定すると、プロパティを設定する順序をフィールドの順番のまま維持できるようになる
- `BuilderStyle.STAGED` の場合、任意プロパティは必須プロパティを設定した後にしか設定できなかった
- しかし、 `BuilderStyle.STAGED_PRESERVING_ORDER` を設定した場合、任意プロパティも含めて設定する順序はフィールドの宣言順序と同じになる
    - 上記例では `text` を任意にしている
    - `STAGED` の場合、任意プロパティである `text` は `number` を設定した後でないと設定できなかった
    - `STAGED_PRESERVING_ORDER` の場合、 `number` の前に `text` を設定できる
        - `HogeBuilders.Text` には `text(String text)` だけでなく `number(int number)` も定義されているので、 `text` を設定せずすぐに `number` を設定できるようになっている

```java:STAGEDの場合
HogeBuilder.hoge()
    .number(1)
    .text("text") // text は任意なので必須プロパティの number より後にしか設定できない
    .build();
```

```java:STAGED_PRESERVING_ORDERの場合
HogeBuilder.hoge()
    .text("text") // text は任意だが、フィールド宣言と同じ順序で設定できる
    .number(1)
    .build();
    
HogeBuilder.hoge()
    .number(1) // text を省略することも可能
    .build();
```

- これには以下のような効果がある
    - 必須プロパティを後から任意プロパティへと変更したとき、実装の順序を変更せずに済む
    - 並びがフィールドと一致するので、目的のプロパティを見つけやすくなる

## インタフェースの調整
### 名前を変更する
```java:Hoge.java
package sandbox.jilt;

import org.jilt.Builder;
import org.jilt.BuilderInterfaces;
import org.jilt.BuilderStyle;

@Builder(style = BuilderStyle.STAGED)
@BuilderInterfaces(outerName = "HogeInterfaces")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeInterfaces.java
package sandbox.jilt;

import java.lang.String;
import javax.annotation.processing.Generated;

@Generated("Jilt-1.6")
public interface HogeInterfaces {
  interface Text {
    Number text(final String text);
  }

  interface Number {
    Optionals number(final int number);
  }

  interface Optionals {
    Hoge build();
  }
}
```

- `@BuilderInterfaces` アノテーションをつけて `outerName` を設定することで、自動生成されるインタフェースの名前を変更できる
- 未指定の場合のデフォルトは、ビルダークラスの名前の後ろに `s` がついたものになる

### 出力先のパッケージを変更する
```java:Hoge.java
@Builder(style = BuilderStyle.STAGED)
@BuilderInterfaces(packageName = "sandbox.jilt.sub")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
package sandbox.jilt.sub;

import java.lang.String;
import javax.annotation.processing.Generated;
import sandbox.jilt.Hoge;

@Generated("Jilt-1.6")
public interface HogeBuilders {
  interface Text {
    Number text(final String text);
  }

  interface Number {
    Optionals number(final int number);
  }

  interface Optionals {
    Hoge build();
  }
}
```

- `@BuilderInterfaces` アノテーションをつけて `packageName` を設定することで、インタフェースの出力先パッケージを変更できる
- 未指定の場合のデフォルトは、ビルダーと同じパッケージになる

### 各プロパティを設定するインタフェースの名前を指定する
```java:Hoge.java
@Builder(style = BuilderStyle.STAGED)
@BuilderInterfaces(innerNames = "*Setter")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
public interface HogeBuilders {
  interface TextSetter {
    NumberSetter text(final String text);
  }

  interface NumberSetter {
    OptionalsSetter number(final int number);
  }

  interface OptionalsSetter {
    Hoge build();
  }
}
```


- `@BuilderInterfaces` アノテーションをつけて `innerNames` を設定することで、各プロパティを設定するインタフェースの名前を指定できる
    - `*` は、各プロパティ名（先頭大文字）に置換される
- 未指定の場合のデフォルトは、プロパティ名の先頭を大文字にしたものになる(`*` を指定した場合と同じ)

### 任意プロパティを設定するインタフェースの名前を変更する
```java:Hoge.java
@Builder(style = BuilderStyle.STAGED)
@BuilderInterfaces(lastInnerName = "NonRequires")
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

```java:HogeBuilders.java
public interface HogeBuilders {
  interface Text {
    Number text(final String text);
  }

  interface Number {
    NonRequires number(final int number);
  }

  interface NonRequires {
    Hoge build();
  }
}
```

- `@BuilderInterfaces` アノテーションをつけて `lastInnerName` を設定することで、最後に任意プロパティを指定するインタフェースの名前を変更できる
- 未指定の場合のデフォルトは、 `STAGED` の場合は `Optionals`、 `STAGED_PRESERVING_ORDER` の場合は `Build`

# メタアノテーション
```java:MyBuilder.java
package sandbox.jilt;

import org.jilt.Builder;
import org.jilt.BuilderInterfaces;
import org.jilt.BuilderStyle;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Builder(style = BuilderStyle.STAGED)
@BuilderInterfaces(innerNames = "*Setter")
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface MyBuilder {
}
```

```java:Hoge.java
@MyBuilder
public class Hoge {
    private final String text;
    private final int number;

    public Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }
}
```

- `@Builder` および `@BuilderInterfaces` を自作のアノテーションにつけて設定をまとめることができる
- 同じ設定を複数個所で使いまわしたい場合に便利

# コンストラクタを private にする
```java:Hoge.java
public class Hoge {
    private final String text;
    private final int number;

    @Builder
    private Hoge(String text, int number) {
        this.text = text;
        this.number = number;
    }

    public static class InnerBuilder extends HogeBuilder {
        @Override
        public Hoge build() {
            return new Hoge(text, number);
        }
    }

    public static InnerBuilder builder() {
        return new InnerBuilder();
    }
}
```

```java:HogeBuilder.java
abstract class HogeBuilder {
  protected String text;

  protected int number;

  public HogeBuilder text(final String text) {
    this.text = text;
    return this;
  }

  public HogeBuilder number(final int number) {
    this.number = number;
    return this;
  }

  public abstract Hoge build();
}
```

```java:実装例
final Hoge hoge = Hoge.builder().text("a").number(1).build();
```

- コンストラクタを `private` にして、インスタンス生成をビルダー経由に限定することができる
- `@Builder` を設定したコンストラクタまたは static メソッドの可視性が `private` の場合、自動生成されるビルダーが以下のように変化する
    - ビルダーが `abstract` (抽象型)になる
    - ビルダーに設定された各プロパティが `protected` なフィールドに格納され、サブクラスから参照できるようになる
    - `build` メソッドが抽象メソッドになる
- 生成対象のクラスで以下のような実装を追加することで、ビルダー経由でインスタンスを生成できるようになる
    - 生成された抽象ビルダークラスを継承した具象ビルダークラスを定義する(実装例の `InnerBuilder`)
    - 具象ビルダークラスを返す、ビルダーのファクトリメソッドを定義する(実装例の `builder` メソッド)

# 参考
- [skinny85/jilt: Java annotation processor for auto-generating Builder (including Staged Builder) pattern classes](https://github.com/skinny85/jilt)
- [知名度は高くないけど 便利なJavaライブラリ集 - Google スライド](https://docs.google.com/presentation/d/1UhEl2S7A-UTocHuosDG6E0pFqEYIMNqU8cXszoxOl2Q/edit#slide=id.g2e15b7c2bf8_0_5)