🐥

【Lombok】@Builder使用時にコンパイルエラーが発生したので原因究明と修正をした

2023/01/16に公開

Lombokとは

Javaコーディングにおけるボイラープレートコードを排除して、見やすく簡潔に実装することができるライブラリです。

詳しくは、別の記事に書いていますので、興味がある方はご参照ください!
https://qiita.com/toranoko92114/items/05cf2dfeab3f2a46ae56

コンパイルエラー

下記のようなコンパイルエラーが発生しました。

エラー: クラス DepartmentDtoのコンストラクタ DepartmentDtoは指定された型に適用できません。
@Builder
^
  期待値: 引数がありません
  検出値: int,String
  理由: 実引数リストと仮引数リストの長さが異なります

エラー発生時のコード

DepartmentDto.java
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@Builder
@RequiredArgsConstructor
public class DepartmentDto {

  // 部署ID
  private int id;

  // 部署名
  private String departmentName;
  
}

原因

結論から申し上げますと、@Builderを利用する際に必要なコンストラクタが定義されていなかったからです。

どういうことか詳しく見ていきます。

まず、@Builderをつけるとコンパイル時に下記コードが自動生成されるようになります。

public DepartmentDto build() {
  return new DepartmentDto(this.id, this.departmentName);
}

build()メソッド内で、DepartmentDtoをインスタンス化する際に、DepartmentDtoに定義した全てのフィールドを引数に持つコンストラクタを指定しています。

つまり、コンパイルされたDepartmentDtoクラスに下記のようなコンストラクタがあれば良いという訳です。

public DepartmentDto(final int id, final String departmentName) {
  this.id = id;
  this.departmentName = departmentName;
}

では、ここで、コンパイルされたDepartmentDtoクラスを見てみましょう。(コンパイルエラーにならないように、@Builderを一時的に外しています)

コンパイルされたDepartmentDtoクラス
DepartmentDto.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.course.apispringbootmybatis.dto;

public class DepartmentDto {
  private int id;
  private String departmentName;

  public int getId() {
    return this.id;
  }

  public String getDepartmentName() {
    return this.departmentName;
  }

  public void setId(final int id) {
    this.id = id;
  }

  public void setDepartmentName(final String departmentName) {
    this.departmentName = departmentName;
  }

  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    } else if (!(o instanceof DepartmentDto)) {
      return false;
    } else {
      DepartmentDto other = (DepartmentDto)o;
      if (!other.canEqual(this)) {
        return false;
      } else if (this.getId() != other.getId()) {
        return false;
      } else {
        Object this$departmentName = this.getDepartmentName();
        Object other$departmentName = other.getDepartmentName();
        if (this$departmentName == null) {
          if (other$departmentName != null) {
            return false;
          }
        } else if (!this$departmentName.equals(other$departmentName)) {
          return false;
        }

        return true;
      }
    }
  }

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

  public int hashCode() {
    int PRIME = true;
    int result = 1;
    result = result * 59 + this.getId();
    Object $departmentName = this.getDepartmentName();
    result = result * 59 + ($departmentName == null ? 43 : $departmentName.hashCode());
    return result;
  }

  public String toString() {
    int var10000 = this.getId();
    return "DepartmentDto(id=" + var10000 + ", departmentName=" + this.getDepartmentName() + ")";
  }

  public DepartmentDto() {
  }
}

すると、DepartmentDtoに定義した全てのフィールドを引数に持つコンストラクタがないんですね。

これがコンパイルエラーになった直接原因です。

@RequiredArgsConstructorでは、必須のメンバ(finalのメンバ)へ値をセットするための引数付きコンストラクタを自動生成することができますが、全フィールドを引数に持つコンストラクタは生成されません。

そのため、コンパイルエラーを解消するには、全フィールドを引数に持つコンストラクターを自動生成してくれる@AllArgsConstructorを付与することにします!

修正後コード

@RequiredArgsConstructor@AllArgsConstructorに変更するだけでなく、
デフォルトコンストラクタを自動生成してくれる@NoArgsConstructorも追加しておくことにします。

DepartmentDto.java
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DepartmentDto {

  // 部署ID
  private int id;

  // 部署名
  private String departmentName;

}

すると、コンパイルエラーが発生しなくなりました!

以上です。

Discussion