概要
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;
}
}
📄バグを減らすために、変数は上書きしない(イミュータブル) で解説しているように、
基本的に 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=true
:final
を付与したフィールドがある場合、「このフィールドは初期化されない可能性がある」と怒られるのでそれを回避
@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
これは以下が付与されたときと同様の効果を発揮するが、
@Setter
を使うことがないという理由からこのアノテーションも使うことはない。
- @Getter
- @Setter
- @RequiredArgsConstructor
- @ToString
- @EqualsAndHashCode
@Value
これは以下の効果を発揮する。
- @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
- 全てのフィールドに
private
とfinal
が付与される
- 全てのフィールドに
- @Getter
- @ToString
- @EqualsAndHashCode
- @AllArgsConstructor
一見良さそうに見えるが、
Getterは本当に外部公開したいフィールドのみにしか付与したくないため、実際はあまり使うことはない。