はじめに
Javaでクラスを書くたびに、getter/setter/コンストラクタ/toString...と定型コードを量産していませんか?
Lombokを使えば、アノテーション1つで定型コード(ボイラープレート)を自動生成できます。
この記事では、Lombokの主要アノテーションをLombokなしのコードと比較しながら解説します。
Lombokとは
LombokはJavaのコンパイル時にコードを自動生成するライブラリです。
- Spring専用ではなく、どんなJavaプロジェクトでも使える
- コンパイル時に処理されるため、実行時のオーバーヘッドはゼロ
- 生成されるコードはIDEの「Structure」や「Outline」で確認可能
導入方法(Maven)
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
導入方法(Gradle)
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
}
scopeがprovided(Maven)/ compileOnly(Gradle)なのは、コンパイル時のみ必要で実行時には不要なためです。
主要アノテーション一覧
| アノテーション | 生成されるもの |
|---|---|
@Getter |
全フィールドのgetter |
@Setter |
全フィールドのsetter |
@NoArgsConstructor |
引数なしコンストラクタ |
@AllArgsConstructor |
全フィールド引数のコンストラクタ |
@RequiredArgsConstructor |
finalフィールドのみ引数にとるコンストラクタ |
@ToString |
toString() |
@EqualsAndHashCode |
equals() + hashCode()
|
@Data |
上記の主要アノテーションをまとめたもの |
@Builder |
Builderパターン |
@Slf4j |
ロガーフィールドの自動生成 |
1. @Getter / @Setter
Lombokなし
public class User {
private String name;
private int age;
private boolean active;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
// boolean型のgetterは isXxx() になる(JavaBeans規約)
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
}
Lombokあり
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class User {
private String name;
private int age;
private boolean active;
}
フィールドが増えても、アノテーションはそのままです。
フィールド単位での指定
クラス全体ではなく、特定のフィールドだけに付けることもできます。
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter
private String name; // getterのみ生成
@Getter @Setter
private int age; // getter + setter両方生成
private boolean active; // 何も生成されない
}
アクセス修飾子の制御
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class User {
private String name;
// setterを外部に公開したくない場合
@Setter(AccessLevel.PROTECTED)
private int age;
// setterを生成しない
@Setter(AccessLevel.NONE)
private String id;
}
2. @NoArgsConstructor / @AllArgsConstructor
Lombokなし
public class Product {
private Long id;
private String name;
private int price;
// 引数なしコンストラクタ
public Product() {}
// 全フィールド引数コンストラクタ
public Product(Long id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
}
Lombokあり
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long id;
private String name;
private int price;
}
JPAの@Entityは引数なしコンストラクタを必要とするため、@NoArgsConstructorはJPAエンティティでよく使われます。
3. @RequiredArgsConstructor
finalフィールドと@NonNullフィールドだけを引数にとるコンストラクタを生成します。
Lombokなし
public class OrderService {
private final OrderRepository orderRepository;
private final NotificationService notificationService;
public OrderService(OrderRepository orderRepository,
NotificationService notificationService) {
this.orderRepository = orderRepository;
this.notificationService = notificationService;
}
}
Lombokあり
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final NotificationService notificationService;
}
Spring Bootではコンストラクタが1つだけの場合、@Autowiredを省略できるため、@RequiredArgsConstructorとの相性が非常に良いです。
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor // コンストラクタインジェクションを自動化
public class TodoService {
private final TodoRepository todoRepository;
// @Autowired 不要!
}
4. @ToString
Lombokなし
public class User {
private String name;
private int age;
@Override
public String toString() {
return "User(name=" + name + ", age=" + age + ")";
}
}
Lombokあり
import lombok.ToString;
@ToString
public class User {
private String name;
private int age;
}
// User(name=太郎, age=25) のように出力される
特定フィールドの除外
パスワードなどの機密情報を出力から除外できます。
import lombok.ToString;
@ToString
public class User {
private String name;
@ToString.Exclude
private String password; // toStringに含まれない
}
5. @EqualsAndHashCode
equals()とhashCode()を全フィールドから自動生成します。
Lombokなし
import java.util.Objects;
public class User {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Lombokあり
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class User {
private String name;
private int age;
}
注意:JPAエンティティでの使用
JPAエンティティで@EqualsAndHashCodeを使う場合は注意が必要です。
import lombok.EqualsAndHashCode;
import jakarta.persistence.*;
@Entity
@EqualsAndHashCode(onlyExplicitlyIncluded = true) // 明示的に指定したフィールドのみ使用
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include // IDのみで比較
private Long id;
private String title;
private boolean completed;
}
全フィールドで比較すると、遅延ロードされた関連エンティティへのアクセスが発生し、パフォーマンス問題やLazyInitializationExceptionの原因になります。
6. @Data
@Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor をまとめたアノテーションです。
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
private String email;
}
これだけで、getter/setter/toString/equals/hashCode/コンストラクタがすべて生成されます。
@Dataを避けるべきケース
| ケース | 理由 | 代替 |
|---|---|---|
| JPAエンティティ |
@EqualsAndHashCodeが全フィールド比較になり危険 |
@Getter @Setterを個別指定 |
| イミュータブルにしたいクラス |
@Setterが生成されてしまう |
@Valueを使う |
7. @Builder
Builderパターンを自動生成します。フィールドが多いクラスで特に有効です。
Lombokなし
public class User {
private String name;
private int age;
private String email;
private String phone;
// Builderクラスを手動で書くと数十行に...
}
Lombokあり
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private String name;
private int age;
private String email;
private String phone;
}
public class Main {
public static void main(String[] args) {
User user = User.builder()
.name("太郎")
.age(25)
.email("taro@example.com")
// phoneは省略可能
.build();
System.out.println(user);
// User(name=太郎, age=25, email=taro@example.com, phone=null)
}
}
8. @Slf4j
ロガーフィールドを自動生成します。
Lombokなし
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TodoService {
private static final Logger log = LoggerFactory.getLogger(TodoService.class);
public void create() {
log.info("Todo作成");
}
}
Lombokあり
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TodoService {
public void create() {
log.info("Todo作成");
}
}
9. @Value(イミュータブルクラス)
@Dataのイミュータブル版です。全フィールドがprivate finalになり、setterは生成されません。
import lombok.Value;
@Value
public class Money {
int amount; // 自動的に private final になる
String currency;
}
public class Main {
public static void main(String[] args) {
Money money = new Money(1000, "JPY");
System.out.println(money.getAmount()); // 1000
System.out.println(money.getCurrency()); // JPY
// money.setAmount(2000); // コンパイルエラー!setterがない
}
}
実践:Spring Boot + Lombokの定番パターン
// Entity
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private boolean completed;
}
// Repository
import org.springframework.data.jpa.repository.JpaRepository;
public interface TodoRepository extends JpaRepository<Todo, Long> {
}
// Service
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor // コンストラクタインジェクション
public class TodoService {
private final TodoRepository todoRepository;
public List<Todo> findAll() {
return todoRepository.findAll();
}
}
// Controller
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/todos")
@RequiredArgsConstructor // コンストラクタインジェクション
public class TodoController {
private final TodoService todoService;
@GetMapping
public List<Todo> findAll() {
return todoService.findAll();
}
}
ポイントは**Service/Controllerでの@RequiredArgsConstructor**です。finalフィールドへのDIを簡潔に書けます。
まとめ
| 用途 | アノテーション |
|---|---|
| getter/setter |
@Getter @Setter
|
| コンストラクタ |
@NoArgsConstructor @AllArgsConstructor @RequiredArgsConstructor
|
| toString | @ToString |
| equals/hashCode | @EqualsAndHashCode |
| 全部入り(DTO向け) | @Data |
| 全部入り(イミュータブル) | @Value |
| Builderパターン | @Builder |
| ロガー | @Slf4j |
| DI(Spring Boot) |
@RequiredArgsConstructor + finalフィールド |
JPAエンティティには@Dataではなく@Getter @Setterを個別に使うのが安全です。
演習問題
問題1 ⭐(基本)
以下のクラスにLombokアノテーションを付けて、手書きのgetter/setter/コンストラクタを削除してください。
public class Book {
private Long id;
private String title;
private String author;
private int price;
public Book() {}
public Book(Long id, String title, String author, int price) {
this.id = id;
this.title = title;
this.author = author;
this.price = price;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
}
模範解答
import lombok.*;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private Long id;
private String title;
private String author;
private int price;
}
問題2 ⭐⭐(応用)
以下の要件を満たすSpring Bootのサービスクラスを、Lombokを使って書いてください。
- クラス名:
BookService -
BookRepositoryとNotificationServiceをDIする -
@Autowiredは使わない - ログ出力用のロガーを持つ
模範解答
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class BookService {
private final BookRepository bookRepository;
private final NotificationService notificationService;
public void create(Book book) {
log.info("本を登録: {}", book.getTitle());
bookRepository.save(book);
notificationService.notify("新しい本が登録されました");
}
}
@RequiredArgsConstructorがfinalフィールドを引数にとるコンストラクタを生成し、Springが自動的にDIしてくれます。
問題3 ⭐⭐⭐(チャレンジ)
以下の要件を満たすOrderRequestクラスをLombokで作成してください。
- フィールド:
productName(String),quantity(int),unitPrice(int),note(String) - Builderパターンで生成できる
- インスタンス生成後は変更不可(イミュータブル)
-
toString()で中身を確認できる -
noteのデフォルト値は"なし"
模範解答
import lombok.Builder;
import lombok.Value;
@Value
@Builder
public class OrderRequest {
String productName;
int quantity;
int unitPrice;
@Builder.Default
String note = "なし";
}
public class Main {
public static void main(String[] args) {
OrderRequest order1 = OrderRequest.builder()
.productName("ノートPC")
.quantity(2)
.unitPrice(150000)
.build();
System.out.println(order1);
// OrderRequest(productName=ノートPC, quantity=2, unitPrice=150000, note=なし)
OrderRequest order2 = OrderRequest.builder()
.productName("マウス")
.quantity(5)
.unitPrice(3000)
.note("ワイヤレスタイプ希望")
.build();
System.out.println(order2);
// OrderRequest(productName=マウス, quantity=5, unitPrice=3000, note=ワイヤレスタイプ希望)
}
}
@Valueはフィールドをprivate finalにしてsetterを生成しないため、イミュータブルになります。@Builder.DefaultでBuilderパターン使用時のデフォルト値を指定できます。
参考
- https://projectlombok.org/features/
- https://projectlombok.org/api/lombok/package-summary
- https://spring.io/guides/gs/accessing-data-jpa
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!