エンジニアを目指す初学者に向けて、わかりやすく解説したブログです。
サイトをリニューアルしました

よく使うlombokの便利アノテーション

概要

SpringBootでWebアプリケーションを構築するときによく利用するlombokの便利なアノテーションを紹介する。

アノテーションは数多く存在するが、筆者はここに書いてあるアノテーションしか使っていないので
初心者の人はこれさえ覚えておけば良い、というものになっている。

@RequiredArgsConstructor

これは finalが付与されているフィールドを引数に取るコンストラクタを自動生成する。

以下の2つのコードは同一である。

@RequiredArgsConstructor
public class User {
  private final String name;
  private Integer age;
}
public class User {
  private final String name;
  private Integer age;

  // finalが付与されている「name」のみを引数に取るコンストラクタ
  public User(String name) {
    this.name = name;
  }
}

📄Arrow icon of a page linkバグを減らすために、変数は上書きしない(イミュータブル) で解説しているように、
基本的に finalを付与したイミュータブルなフィールドしか扱わないため、
コンストラクタ系のアノテーションはほぼこれ一択。

@NoArgsConstructor

引数なしのコンストラクタを自動生成する。

@NoArgsConstructor
public class User {
  private final String name;
  private Integer age;
}
public class User {
  private final String name;
  private Integer age;

  public User() {
  }
}

これはAPIのレスポンスをマッピングするオブジェクトでやむを得ず利用することが多い。

Jacksonはデフォルトコンストラクタ(引数なしコンストラクタ)を必要とするので、それだけのために利用する。

実際は以下の設定を入れて使うことがほとんど。

@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
  • AccessLevel.PRIVATE:普通の実装でこのコンストラクタが勝手に使われないように、クラス外からデフォルトコンストラクタにアクセスできないようにする
  • force=truefinalを付与したフィールドがある場合、「このフィールドは初期化されない可能性がある」と怒られるのでそれを回避

@Getter

これは get〇〇()というメソッドを自動生成するアノテーション。

  • クラス全体に付与:全てのフィールドのGetterを生成
  • フィールドに付与:そのフィールドのGetterを生成

上記を使い分けて、本当に必要なフィールドのみ外部に公開するようにする。

■クラス全体に付与

@Getter
public class User {
  private String name;
  private Integer age;
}

public class User {
  private String name;
  private Integer age;

  public String getName() {
    return this.name;
  }

  public Integer getAge() {
    return this.age;
  }
}

■フィールドに部分的に付与

public class User {
  @Getter
  private String name;
  private Integer age;
}
public class User {
  private String name;
  private Integer age;

  public String getName() {
    return this.name;
  }
}

@ToString

これは toString()メソッドを自動生成するアノテーション。

オブジェクトの中身をログ出力したいときなどによく利用する。

@ToString
public class User {
  private String name;
  private Integer age;
}
public class User {
  private String name;
  private Integer age;

  public String toString() {
    return "User(name=" + this.name + ", age=" + this.age + ")";
  }
}

@EqualsAndHashCode

これは、 equals()メソッドと hashCode()メソッドを自動生成する。

オブジェクトの等価比較が必要なときはこのアノテーションを付与することで、簡単に実装することができる。

@EqualsAndHashCode
public class User {
  private String name;
  private Integer age;
}
public class User {
  private String name;
  private Integer age;

  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof User)) {
      return false;
    }
    final User other = (User) o;
    if (!other.canEqual((Object) this)) {
      return false;
    }
    final Object this$name = this.name;
    final Object other$name = other.name;
    if (this$name == null ? other$name != null : !this$name.equals(other$name)) {
      return false;
    }
    final Object this$age = this.age;
    final Object other$age = other.age;
    if (this$age == null ? other$age != null : !this$age.equals(other$age)) {
      return false;
    }
    return true;
  }

  protected boolean canEqual(final Object other) {
    return other instanceof User;
  }

  public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final Object $name = this.name;
    result = result * PRIME + ($name == null ? 43 : $name.hashCode());
    final Object $age = this.age;
    result = result * PRIME + ($age == null ? 43 : $age.hashCode());
    return result;
  }
}

@Builder

これはBuilderパターンのコンストラクタを生成することができる。

コンストラクタの引数が多い場合はこれを使うと書きやすくなる。

@Builder
public class User {
  private String name;
  private Integer age;
}
public class User {
  private String name;
  private Integer age;

  User(String name, Integer age) {
    this.name = name;
    this.age = age;
  }

  public static UserBuilder builder() {
    return new UserBuilder();
  }

  public static class UserBuilder {
    private String name;
    private Integer age;

    UserBuilder() {
    }

    public UserBuilder name(String name) {
      this.name = name;
      return this;
    }

    public UserBuilder age(Integer age) {
      this.age = age;
      return this;
    }

    public User build() {
      return new User(this.name, this.age);
    }

    public String toString() {
      return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ")";
    }
  }
}

使い方

このような書き方でオブジェクトを生成できるようになる。

final User user = User.builder()
    .name("hoge")
    .age(20)
    .build();

注意点

引数の順番を間違えにくいというメリットはあるが、
一方でフィールドを書くのを忘れた場合、nullで初期化されてしまうので使い所には注意すること。

筆者はファクトリメソッドを用意することがほとんどなので、プライベートにして使うことが多い。

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
public class User {
  private final String name;
  private final Integer age;

  public static User of(final String name, final Integer age) {
    if (age < 0) {
      throw new RuntimeException("年齢は0歳以上です");
    }
    return User.builder()
        .name(name)
        .age(age)
        .build();
  }
}
// 利用側コード
final User user = User.of("hoge", 30);

@Slf4j

これはロガーを使えるようにするもの。

log.info()log.error()などを使えるようにする。

使っていないアノテーション

筆者が使っていないアノテーションも紹介しておく。

@Setter

これは set〇〇()メソッドを自動生成するアノテーションだが、
基本的に全ての変数はイミュータブルが望ましいため使うことはまずない。

@Data

Icon in a page link@Data

これは以下が付与されたときと同様の効果を発揮するが、
@Setterを使うことがないという理由からこのアノテーションも使うことはない。

  • @Getter
  • @Setter
  • @RequiredArgsConstructor
  • @ToString
  • @EqualsAndHashCode

@Value

Icon in a page link@Value

これは以下の効果を発揮する。

  • @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
    • 全てのフィールドに privatefinal が付与される
  • @Getter
  • @ToString
  • @EqualsAndHashCode
  • @AllArgsConstructor

一見良さそうに見えるが、
Getterは本当に外部公開したいフィールドのみにしか付与したくないため、実際はあまり使うことはない。