JavaとLombokの@Withでイミュータブルなデータ型を生成する

エキサイト株式会社エンジニアの佐々木です。2021年アドベントカレンダー5日目を担当します。 サービスを開発している上で、データをなるべくイミュータブルにするというのは、バグを作り込まない上で重要だと思います。Java15でRecord型が導入されていますが、まだ未対応なライブラリがあると思います。今回はLombokでは@Withを使って簡単に実装できます。

ミュータブルなデータ型

ミュータブルなデータ型は、Lombokでは@Dataを使って簡単にできます。

@Data
class Form {
    private final Long id;
    private final String name;
}

使用時:
Form form = new Form();
form.setId(1L);
form.setName("sample");

form.setId(2L);   // form.id が 2にかわる
form.setName("sample2"); // form.name が 2に変わってしまう

コード見れば明らかですが、途中でオブジェクトの状態が変わっています。メソッドの引数で渡して、再代入等を特にしていない場合でも変わってしまうので、バグの1つの原因になります。

イミュータブルなデータ型(@Value)

@Value
class Form {
    private final Long id;
    private final String name;
}

使用時:
Form form = new Form(1L, "sample");

form.setId(2L);  // メソッドが存在しないので、エラーになる
form.setName("sample2"); // メソッドが存在しないので、エラーになる

Form form2 = new Form(2L, "sample2"); // 代入 or 再代入をする

コンストラクタを使用したときだけ値を設定できるようにすると、1度定義したオブジェクトの状態は途中で変えることができません。途中で変えるには、再度コンストラクタ呼び出しを行い、再代入等が必要です。しかし、引数が少ないときはいいですが、多くなってくると辛くなってきます。そこで@Withが登場します。

イミュータブルなデータ型(@With)

@With
@Value
class Form {
    private final Long id;
    private final String name;
}

使用時
Form form = new Form(1L, "sample");
form.withId(2L);
form.withName("sample");

log.info("{}", form);  // Form(id=1, name=sample);

Form form2 = form.withId(2L).withName("sample2");
log.info("{}", form);  // Form(id=2, name=sample2);   

withId(), withName() を使用して値変更しても、formオブジェクトの値は変更されていません。setterとは違い、新規オブジェクトが返ってきます。では、@Withがどういう処理をしているのかをdelombokをして見ていきます。

public Form withId(Long id) {
    return this.id == id ? this : new Form(id, this.name);
}

public Form withName(String name) {
    return this.name == name ? this : new Form(this.id, name);
}

このようなコードが裏側で実行されています。値が同じ時は自分の参照を返し、値が変更されている場合は、新規のオブジェクトを生成して返しています。これでイミュータブルなオブジェクトになって安全です。

まとめ

Java15でRecord型が導入され、イミュータブルなオブジェクト専用の型が追加されています。状態が変更される副作用を抑えるべく導入されているかと思います。周辺ライブラリが対応するまではLombokを駆使して、ミュータブル、イミュータブルなオブジェクト生成の使い分けをする感じになるかと思います。

最後に

引き続きエキサイトホールディングスのアドベントカレンダーをお楽しみいただければ幸いです。 Calendar for エキサイトホールディングス Advent Calendar 2021 | Advent Calendar 2021 - Qiita

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。

カジュアル面談はmeetyを公開していますので、よろしくお願いいたします! meety.net

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com