埼玉在住エンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことについて残していきたいと思っています。https://ikujip.jpの開発も行っているため、そこで使った知識なども載せられればと思います。

@Builderのクラスに引数なしのコンストラクタを設置したい

@BuilderはLombokにより使えるライブラリで、
エンティティ呼び出し時にbuilderの生成を簡単にしてくれます。

仕組みとして、例えば

@Builder
public class Test() {
    private int id;
    private String name;
}

とすれば、実際には以下のようなコンストラクタが作られており、

public class Test() {
    private int id;
    private String name;
 
    public Test(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}

以下のように簡単にビルダーできます。

Test test = Test.builder().name('TEST').build();

便利だな〜、と思っていたのですが、このままでは不都合なことが...
例えばリポジトリクラスを通じて、findByName();とかをしようとすると、
以下のようなエラーメッセージが出て上手く動かないと思います。

no default constructor for entity

あれ?
@Builderでコンストラクタが作られてるのでは...?
と思ったのですが、findで求められているコンストラクタは引数なしのコンストラクタです。
(public Test(){};のような)

普通インスタンスを作成する時に、
クラス中にコンストラクタの定義が存在していればその定義を読み込みますが、
存在していなければ、引数なしのコンストラクタを読み込みます。

今回は@Builderにより引数ありのコンストラクタが作られていたので、
引数なしのコンストラクタは呼ばれません。
そうすると、
求められているコンストラクタ → 引数なしのコンストラクタ(デフォルトのコンストラクタ)
なのに、
呼ばれたコンストラクタ → 引数ありのコンストラクタ

となり、エラーが出てしまうのです。

じゃあ@Builderで引数ありのコンストラクタが作られるなら、
引数なしのデフォルトのコンストラクタも用意しよう!
と思い、単純に

Public Test();

だけを用意しても残念ながらダメです。
今度はコンパイル時に、

コンストラクタ Testは指定された型に適用できません。...実引数リストと仮引数リストの長さが異なります

的なエラーが出てしまいます。

@Builderでは、すでに同じ名前のメソッドが存在している時、
追加でメソッドを作らないのです。
片方を立てれば片方が立たず...
さあどうしましょう。
buiderを諦めようか...

と思いましたが、
こんな場合は、Tolerateアノテーションを使えば回避できることを発見!

@Tolerate
Public Test();

このアノテーションを使えば、
エンティティ内にコンストラクタが定義されている場合でも、
@Builderで引数ありのコンストラクタが定義されるのです!