📘

LombokのBuilderパターンの使い方

2024/02/05に公開

はじめに

LombokのBuilderを触る機会があったので、後々のために色々まとめておこうという記事です。

環境

  • openjdk 11.0.12 2021-07-20
  • lombok 1.18.22

そもそもLombokってなに?

LombokとはJavaの冗長なコードを簡潔にしてくれるライブラリです。
簡単なアノテーションを書くだけで、GetterやSetterを生成したり、Builderとして機能するクラスを実装できるなどの便利機能が沢山用意されています。

https://github.com/projectlombok/lombok

Builderパターンってなに?

Builderパターンとは、インスタンスを生成する際にコンストラクタではなく、Builderと呼ばれるクラスを使用してインスタンスを生成する手法のことです。

以下、簡単なコード例です。

// コンストラクタで生成
var user = new User("Taro");

// Builderで生成
var user = User.builder()
    .name("Jiro")
    .build;

Builderパターンのメリット

コンストラクタを使う場合に発生する、以下の問題を解決できます。

  • コンストラクタで値をインスタンスを生成する場合、引数の多いと引数の順番を覚える必要があり、ミスをしやすい。
  • インスタンス生成の際に任意の値があると、何かしら適当な値を渡す必要が出てくる。

LombokのBuilderパターンの使い方

基本形

まずは基本です。
Builderパターンを使ったクラスの定義とインスタンスの生成を行います。

Builderクラスの定義はこちら。

import lombok.*;

// @Builderというアノテーションをつけると、Builderとして機能します。
@Builder
public class UserDataClass {
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

インスタンスの生成は、以下のようなコードで可能になります。

public static void main(String[] args){
    var user = UserDataClass.builder()
        .id(1)
        .name("name")
        .email("email@example.com")
        .address("address")
        .build();
}

ただし、このままだと都合が良くない部分があるため、少しづつ機能を追加して解決したいと思います。

print時の表示処理を追加する

基本形のUserDataClassをprintすると、以下のような表示になります。
インスタンスの値を見たいといった場合に、これでは効果を発揮しません。

UserDataClass@958bde30

Builderクラスに@ToStringを追加すると、この問題を解決できます。

import lombok.*;

@Builder
@ToString // @ToStringを追加
public class UserDataClass {
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

するとprintした際に各パラメータを詳細に表示した結果になります。

UserDataClass(id=1, name=name, email=email@example.com, address=address)

equalsとhashCodeを実装します。

@EqualsAndHashCodeをつけることで、equalsとhashCodeメソッドを実装できます。
*equalsとhashCodeの適正な実装はオブジェクトの要件によって異なります。アノテーションを使うか、自分で実装するかは要件に応じて適宜判断してください。

import lombok.*;

@Builder
@ToString
// @EqualsAndHashCodeをつけると、equalsとhashCodeが生成される
@EqualsAndHashCode
public class UserDataClass {
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

equalsとhashCodeを実装する意味に関しては、JavaにおけるequalsとhashCodeを理解するという記事に詳しく記載されているので、気になる方は見てみてください。

Getterを実装する

@Getterを設定すると、各プロパティのGetterが自動的に生成されます。
※Setterを実装したい場合は、@Setterを設定すれば可能です。

import lombok.*;

@Builder
@Getter // @Getterをつけると、各プロパティのgetterが生成される
@ToString
@EqualsAndHashCode
public class UserDataClass {
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

インスタンス生成の方法をBuilderパターンのみに固定する

LombokのBuilderを使用すると、設定したクラスのコンストラクタのアクセサがpackage privateで生成されます。
Builderパターンを使っている状態でコンストラクタが呼び出せる状態は、インスタンス生成の方法を複数持つことになり好ましくないので、インスタンスの生成方法をBuilderを使う方法のみに限定します。

import lombok.*;

@Builder
 // コンストラクタのアクセスをprivateにすることで、コンストラクタを外から呼び出せないように設定
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
@EqualsAndHashCode
public class UserDataClass {
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

null判定を追加

LombokのBuilderは、値を指定せずにインスタンスを生成した場合、プロパティにnullが設定された状態になります。

処理中に必ず値を設定したいプロパティに関して、必須の制約をかけたい場合には@NonNullを設定します。

import lombok.*;

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
@EqualsAndHashCode
public class UserDataClass {
    // idに必須の設定を追加
    @NonNull
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

この設定を行うことで、インスタンス生成時に値を設定しなかった場合にNullPointerExceptionが投げられるようになります。

var user = UserDataClass.builder()
    //.id(1)  <-- 必須プロパティを設定しないことで例外が発生するように
    .name("name")
    .email("email@example.com")
    .address("address")
    .build();

個人的には、コンパイルエラー吐くようにしてくれると嬉しかったなぁ...などと

最終形

最終的なコードは以下になります。
さらに機能をつけたい場合は、Lombokの様々なアノテーションをつけることで実現できます。

import lombok.*;

@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@ToString
@EqualsAndHashCode
public class UserDataClass {
    @NonNull
    private final Integer id;
    private final String name;
    private final String email;
    private final String address;
}

さいごに

Builderパターン、DataClassに該当するクラスで適応する際には特に力を発揮するので、機会があればお試しあれ。

Discussion