【Java】Lombokを使ってボイラープレートコード排除

# お題
Javaでは例えば↓のように、いわゆるJavaBeansを作ると、フィールドの数に応じてけっこうなコード量になる。

```java
public class Book {
    private int id;
    private String name;
    private int price;
    private String publishDate;

    public Book() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getPublishDate() {
        return publishDate;
    }

    public void setPublishDate(String publishDate) {
        this.publishDate = publishDate;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", publishDate='" + publishDate + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return id == book.id &&
                price == book.price &&
                Objects.equals(name, book.name) &&
                Objects.equals(publishDate, book.publishDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, price, publishDate);
    }
}
```

これが、[Lombok](https://projectlombok.org)を使うと、↓のように書くだけで↑と同じメソッドを持つコードになる。

```java
import lombok.Data;

@Data
public class Book {
    private int id;
    private String name;
    private int price;
    private String publishDate;
}
```

便利なので、少し調べてみることにする。

# 開発環境
### # OS
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
```

### # Java
```
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
```
### # IDE
みんな大好き[IntelliJ IDEA](https://www.jetbrains.com/idea/)

# 実践

## 導入手順

### Mavenプロジェクト生成

```
mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.example -DartifactId=try-java-lombok
```

### pom.xml にLombokを追加

```xml
  <dependencies>
　　　　　〜〜〜
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
```

### IntelliJにLombokプラグイン追加

![001.png](https://qiita-image-store.s3.amazonaws.com/0/180599/0dd2df82-3b90-361e-7f8d-78e86307d557.png)

### Lombokによりどのようなコードになるかを確認するためJarをダウンロード

https://projectlombok.org/download

## 各アノテーションを試す

全ソースは↓
https://github.com/sky0621/try-java-lombok

### `@Getter`と`@Setter`

その名の通り、変数に付加するとゲッターセッターを自動生成。

#### 【Lombokアノテーション付与ソース】

```java
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GetterSetter {
    private String text;
}
```

#### 【実行時のソース】

```java
public class GetterSetter {
    private String text;

    @java.lang.SuppressWarnings("all")
    public String getText() {
        return this.text;
    }

    @java.lang.SuppressWarnings("all")
    public void setText(final String text) {
        this.text = text;
    }
}
```

### `@ToString`

その名の通り。

#### 【Lombokアノテーション付与ソース】

```java
@lombok.ToString
public class ToString {
    private String text;
}
```

#### 【実行時のソース】

```java
public class ToString {
    private String text;

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public java.lang.String toString() {
        return "ToString(text=" + this.text + ")";
    }
}
```

### `@EqualsAndHashCode`

「equals」メソッド及び「hashCode」メソッド生成。

#### 【Lombokアノテーション付与ソース】

```java
@lombok.EqualsAndHashCode
public class EqualsAndHashCode {
    private String text;
}
```

#### 【実行時のソース】

```java
public class EqualsAndHashCode {
    private String text;

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsAndHashCode)) return false;
        final EqualsAndHashCode other = (EqualsAndHashCode) o;
        if (!other.canEqual((java.lang.Object) this)) return false;
        final java.lang.Object this$text = this.text;
        final java.lang.Object other$text = other.text;
        if (this$text == null ? other$text != null : !this$text.equals(other$text)) return false;
        return true;
    }

    @java.lang.SuppressWarnings("all")
    protected boolean canEqual(final java.lang.Object other) {
        return other instanceof EqualsAndHashCode;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final java.lang.Object $text = this.text;
        result = result * PRIME + ($text == null ? 43 : $text.hashCode());
        return result;
    }
}
```

### `@NoArgsConstructor`

引数なしコンストラクタを生成

#### 【Lombokアノテーション付与ソース】

```java
@lombok.NoArgsConstructor
public class NoArgsConstructor {
}
```

#### 【実行時のソース】

```java
public class NoArgsConstructor {
	@java.lang.SuppressWarnings("all")
	public NoArgsConstructor() {
	}
}
```

### `@AllArgsConstructor`

全引数指定コンストラクタを生成

#### 【Lombokアノテーション付与ソース】

```java
@lombok.AllArgsConstructor
public class AllArgsConstructor {
    private int id;
    private String name;
    private Date created;
}
```

#### 【実行時のソース】

```java
public class AllArgsConstructor {
    private int id;
    private String name;
    private Date created;

    @java.lang.SuppressWarnings("all")
    public AllArgsConstructor(final int id, final String name, final Date created) {
        this.id = id;
        this.name = name;
        this.created = created;
    }
}
```

### `@RequiredArgsConstructor`

特別な処理が必要なフィールドごとに1つのパラメータを持つコンストラクタを生成

#### 【Lombokアノテーション付与ソース】

```java
@lombok.RequiredArgsConstructor
public class RequiredArgsConstructor {
    @NonNull
    private int id;

    @NonNull
    private String text;

    private Data created;
}
```

#### 【実行時のソース】

```java
import lombok.Data;
import lombok.NonNull;

public class RequiredArgsConstructor {
    @NonNull
    private int id;
    @NonNull
    private String text;
    private Data created;

    @java.lang.SuppressWarnings("all")
    public RequiredArgsConstructor(@NonNull final int id, @NonNull final String text) {
        if (text == null) {
            throw new java.lang.NullPointerException("text is marked @NonNull but is null");
        }
        this.id = id;
        this.text = text;
    }
}
```

### `@Data`

上記までで紹介したアノテーションのうち、一挙に`@ToString`、`@EqualsAndHashCode`、`@Getter`を全フィールドに付与し、`@Setter`を全ての`final`フィールドに付与。あと、`@RequiredArgsConstructor`も付与。

#### 【Lombokアノテーション付与ソース】

```java
@lombok.Data
public class Data {
    private int id;
    private String name;
    private int price;
    private String publishDate;
}
```

#### 【実行時のソース】

```java
public class Data {
    private int id;
    private String name;
    private int price;
    private String publishDate;

    @java.lang.SuppressWarnings("all")
    public Data() {
    }

    @java.lang.SuppressWarnings("all")
    public int getId() {
        return this.id;
    }

    @java.lang.SuppressWarnings("all")
    public String getName() {
        return this.name;
    }

    @java.lang.SuppressWarnings("all")
    public int getPrice() {
        return this.price;
    }

    @java.lang.SuppressWarnings("all")
    public String getPublishDate() {
        return this.publishDate;
    }

    @java.lang.SuppressWarnings("all")
    public void setId(final int id) {
        this.id = id;
    }

    @java.lang.SuppressWarnings("all")
    public void setName(final String name) {
        this.name = name;
    }

    @java.lang.SuppressWarnings("all")
    public void setPrice(final int price) {
        this.price = price;
    }

    @java.lang.SuppressWarnings("all")
    public void setPublishDate(final String publishDate) {
        this.publishDate = publishDate;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (!(o instanceof Data)) return false;
        final Data other = (Data) o;
        if (!other.canEqual((java.lang.Object) this)) return false;
        if (this.getId() != other.getId()) return false;
        final java.lang.Object this$name = this.getName();
        final java.lang.Object other$name = other.getName();
        if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
        if (this.getPrice() != other.getPrice()) return false;
        final java.lang.Object this$publishDate = this.getPublishDate();
        final java.lang.Object other$publishDate = other.getPublishDate();
        if (this$publishDate == null ? other$publishDate != null : !this$publishDate.equals(other$publishDate)) return false;
        return true;
    }

    @java.lang.SuppressWarnings("all")
    protected boolean canEqual(final java.lang.Object other) {
        return other instanceof Data;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        result = result * PRIME + this.getId();
        final java.lang.Object $name = this.getName();
        result = result * PRIME + ($name == null ? 43 : $name.hashCode());
        result = result * PRIME + this.getPrice();
        final java.lang.Object $publishDate = this.getPublishDate();
        result = result * PRIME + ($publishDate == null ? 43 : $publishDate.hashCode());
        return result;
    }

    @java.lang.Override
    @java.lang.SuppressWarnings("all")
    public java.lang.String toString() {
        return "Data(id=" + this.getId() + ", name=" + this.getName() + ", price=" + this.getPrice() + ", publishDate=" + this.getPublishDate() + ")";
    }
}
```

# まとめ
まだまだ便利アノテーションはあるのだけど、今日は時間切れ。
残りは、また後日。