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

# お題
[前回](https://qiita.com/sky0621/items/7772eb24b26c4208d88f)の続きで、Lombokによるボイラープレート機能の紹介。

# 開発環境
### # 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/)

# 実践

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

### `@Builder`

[Builderパターン](https://sourcemaking.com/design_patterns/builder)のソースを生成

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

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

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

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

    @java.lang.SuppressWarnings("all")
    Builder(final int id, final String name, final int price, final String publishDate) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.publishDate = publishDate;
    }


    @java.lang.SuppressWarnings("all")
    public static class BuilderBuilder {
        @java.lang.SuppressWarnings("all")
        private int id;
        @java.lang.SuppressWarnings("all")
        private String name;
        @java.lang.SuppressWarnings("all")
        private int price;
        @java.lang.SuppressWarnings("all")
        private String publishDate;

        @java.lang.SuppressWarnings("all")
        BuilderBuilder() {
        }

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

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

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

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

        @java.lang.SuppressWarnings("all")
        public Builder build() {
            return new Builder(id, name, price, publishDate);
        }

        @java.lang.Override
        @java.lang.SuppressWarnings("all")
        public java.lang.String toString() {
            return "Builder.BuilderBuilder(id=" + this.id + ", name=" + this.name + ", price=" + this.price + ", publishDate=" + this.publishDate + ")";
        }
    }

    @java.lang.SuppressWarnings("all")
    public static BuilderBuilder builder() {
        return new BuilderBuilder();
    }
}
```

#### 【使い方】

```java
public class Main {
    public static void main(String... args){
        System.out.println(
                Builder.builder()
                        .id(100)
                        .name("名前")
                        .price(937)
                        .publishDate("2018-11-09")
                        .build().toString());
    }
}
```

### `@Value`

不変クラスを生成

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

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

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

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

    @java.lang.SuppressWarnings("all")
    public Value(final int id, final String name, final int price, final String publishDate) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.publishDate = publishDate;
    }

    @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.Override
    @java.lang.SuppressWarnings("all")
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (!(o instanceof Value)) return false;
        final Value other = (Value) o;
        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.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 "Value(id=" + this.getId() + ", name=" + this.getName() + ", price=" + this.getPrice() + ", publishDate=" + this.getPublishDate() + ")";
    }
}
```

### `@Cleanup`

ファイルオープンなど、処理後のクローズロジックを付加してくれる。
が、Java7以降ならtry-with-resourcesを使うべきなので、使うとしたらJava6までかな。

```java
public class Cleanup {
    public static void main(String[] args) throws IOException {
        @lombok.Cleanup InputStream in = new FileInputStream(args[0]);
        @lombok.Cleanup OutputStream out = new FileOutputStream(args[1]);
        byte[] b = new byte[10000];
        while (true) {
            int r = in.read(b);
            if (r == -1) break;
            out.write(b, 0, r);
        }
    }
}
```

```java
import java.io.*;

public class Cleanup {
    public static void main(String[] args) throws IOException {
        InputStream in = new FileInputStream(args[0]);
        try {
            OutputStream out = new FileOutputStream(args[1]);
            try {
                byte[] b = new byte[10000];
                while (true) {
                    int r = in.read(b);
                    if (r == -1) break;
                    out.write(b, 0, r);
                }
            } finally {
                if (java.util.Collections.singletonList(out).get(0) != null) {
                    out.close();
                }
            }
        } finally {
            if (java.util.Collections.singletonList(in).get(0) != null) {
                in.close();
            }
        }
    }
}
```

### `@Log` (java.util.logger.Logger)

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

```java
@lombok.extern.java.Log
public class LogExample {
    public static void main(String... args) {
        log.severe("Something's wrong here");
    }
}
```

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

```java
public class LogExample {
    @java.lang.SuppressWarnings("all")
    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

    public static void main(String... args) {
        log.severe("Something\'s wrong here");
    }
}
```

### `@NonNull`

メソッド引数のnullチェック挿入。
ただし、デフォルトは`NullPointerException`がスローされる。
`IllegalArgumentException`に変えることは可能。

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

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

    public void setNonNull(@lombok.NonNull Book book) {
        this.name = book.getName();
    }
}
```

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

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

    public void setNonNull(@lombok.NonNull Book book) {
        if (book == null) {
            throw new java.lang.NullPointerException("book is marked @NonNull but is null");
        }
        this.name = book.getName();
    }
}
```

# まとめ
ひとまず基本的な機能のいくつかをピックアップして紹介した。ちなみに本家は↓
https://projectlombok.org/features/all

その他、実験的な機能が下記に紹介されている。
https://projectlombok.org/features/experimental/all