本ページはプロモーションが含まれています

Java入門

【2026年版】Javaのrecordとは?classとの違い・使い方・Spring Bootでの使いどころを完全解説

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

「Javaのクラス定義、もっとシンプルにならないかな…」

長年Javaで開発をしていると、データを保持するためだけのクラス、いわゆるDTO(Data Transfer Object)を作るたびに、同じようなコードを何度も書いていることに気づきます。

フィールドを宣言し、コンストラクタを作り、getterを一つひとつ用意して、おまけにequals()hashCode()toString()までオーバーライドする。

この作業に、私は少しうんざりしていました。

あるプロジェクトでJava14から導入されたrecordを試したところ、これまで数十行にわたって書いていたDTOが、たった1行で定義できたのです。

コード量が劇的に減り、クラスの目的が「データを保持すること」だと一目でわかるようになりました。

この経験は、私のJavaコーディングを大きく変えるきっかけになりました。

言葉より先に、実際にどれだけ省略できるのか見てもらうのが早いです。

xy座標だけを持つ「点」のクラスを、従来の書き方だと以下のようにgetter・コンストラクタ・equals()hashCode()toString()を1つずつ手で書く必要があります。

public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { return x; }
    public int getY() { return y; }

    @Override
    public boolean equals(Object o) { /* 省略 */ }
    @Override
    public int hashCode() { /* 省略 */ }
    @Override
    public String toString() { /* 省略 */ }
}

これがrecordを使うと、getter・コンストラクタ・equals()hashCode()toString()がすべて自動生成され、次の1行だけで済みます。

public record Point(int x, int y) {}

この記事では、recordの基本構文からclassとの違い・コンパクトコンストラクタ・Spring Bootでのrecord活用・使うべきケース/避けるべきケースまで、2026年時点の現場知識をまとめて解説します。

この記事は、かつての私と同じくrecordのメリットや書き方を知りたい方に向けて書いています。

  • クラス定義の冗長さに悩んでいる
  • recordの具体的な使い方が知りたい
  • classとrecordをどう使い分ければ良いか分からない

こんな悩みを抱えているなら、ぜひ読み進めてください。

この記事を読み終える頃には、Javaのrecordを完全に理解し、あなたのプロジェクトで自信を持って活用できるようになるでしょう。

結論

recordは「不変(イミュータブル)なデータを保持する専用クラス」です。

DTO・VO・APIのリクエスト/レスポンスのようにデータを保持するだけの目的ならrecordを、状態が変化する・継承が必要・複雑なロジックを持つなら従来のclassを使う、というのが判断基準になります。

public record Point(int x, int y) {}の1行だけで、getter・コンストラクタ・equals()hashCode()toString()がすべて自動生成されます。

Javaのrecordとは?【簡単に言うとデータ専用クラス】

Javaのrecordがデータ専用クラスであることを示す図解

Javaのrecordは、不変(イミュータブル)なデータを保持するための専用クラスです。

これまでデータを扱うためだけに作っていたクラスの、面倒な記述を大幅に削減してくれます。

Java 14(2020年)でプレビュー機能として導入され、Java 16(2021年3月)で正式にリリースされました。

2026年8月時点ではJava 17・21・25(いずれもLTS版)で標準的に利用できます。

recordを使えば、データの定義に集中でき、より本質的なロジックの記述に時間を使えるようになります(仕様の詳細は Oracle公式の Records ドキュメント も参照)。

recordが登場した背景(Java14で導入された理由)

recordが登場する前のJavaでは、データを保持するクラスを作るのに多くの「お決まりのコード(ボイラープレート)」が必要でした。

例えば、x座標とy座標を持つ「点」を表すクラスを作るだけでも、

  • フィールド(x, y
  • コンストラクタ
  • 各フィールドのgetterメソッド(getX(), getY()
  • equals()メソッド(値が同じか比較するため)
  • hashCode()メソッド(ハッシュ値の計算)
  • toString()メソッド(デバッグ用の文字列出力)

これらすべてを自分で実装する必要があったのです。これらは決まりきった作業でありながら、バグの温床にもなりやすく、開発者の負担になっていました。

recordは、こうした課題を解決するために生まれました。

recordが解決する「クラス定義の冗長さ」とは

言葉で説明するよりも、実際のコードを見てもらうのが一番分かりやすいでしょう。先ほどの「点」を、従来のclassで表現してみます。

import java.util.Objects;

public final class Point {

    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Point[" +
                "x=" + x +
                ", y=" + y +
                ']';
    }
}

たった2つのデータを保持したいだけなのに、40行近くのコードが必要になります。これがrecordが解決しようとした「クラス定義の冗長さ」です。

recordの基本構文と定義方法

では、同じPointクラスをrecordで定義するとどうなるのでしょうか。驚くほどシンプルになります。

public record Point(int x, int y) {}

たったこれだけです。

classキーワードをrecordに変え、クラス名の後に()を付けて、保持したいデータ(フィールド)をカンマ区切りで宣言します。

これだけで、先ほどの40行近いコードと全く同じ機能を持つクラスが定義できてしまいます。

具体的には、この1行のrecord定義によって、以下の要素がコンパイラによって自動的に生成されます。

record

  • private finalなフィールド(xy
  • 全フィールドを引数に持つコンストラクタ
  • 各フィールドのアクセサメソッド(x()y()
  • equals()hashCode()toString()メソッド

驚くほど簡潔だと思いませんか。これがrecordの大きな魅力です。

recordの使い方【サンプルコード付き】

recordのサンプルコード付き使い方を示す図解

recordの基本的な魅力が分かったところで、次は具体的な使い方を見ていきましょう。

インスタンスの生成やメソッドの追加など、実践的な使い方をサンプルコードと共に解説します。

最もシンプルなrecordの例

先ほどのPointレコードを実際に使ってみましょう。インスタンスの生成方法は、通常のクラスと全く同じです。

// Pointレコードの定義
public record Point(int x, int y) {}

// インスタンスの生成
var p1 = new Point(10, 20);
var p2 = new Point(10, 20);
var p3 = new Point(30, 40);

// フィールドへのアクセス
System.out.println("p1のx座標: " + p1.x()); // 出力: p1のx座標: 10
System.out.println("p1のy座標: " + p1.y()); // 出力: p1のy座標: 20

// 自動生成されたtoString()の確認
System.out.println(p1); // 出力: Point[x=10, y=20]

// 自動生成されたequals()の確認
System.out.println("p1とp2は同じか: " + p1.equals(p2)); // 出力: p1とp2は同じか: true
System.out.println("p1とp3は同じか: " + p1.equals(p3)); // 出力: p1とp3は同じか: false

インスタンス化はnewを使い、フィールドへのアクセスはフィールド名()という形式のメソッドで行います。

getX()ではなくx()となる点に注意してください。

recordのコンストラクタ・メソッドを定義する方法(コンパクトコンストラクタ)

recordは自動でコンストラクタを生成してくれますが、独自のコンストラクタやメソッドを追加することも可能です。

あわせて読む

独自のコンストラクタ(コンパクトコンストラクタ)

コンストラクタで引数のバリデーションを行いたい場合があります。

recordには、そのための特別な「コンパクトコンストラクタ」という構文が用意されています。

public record User(String name, int age) {
    // コンパクトコンストラクタ
    public User {
        if (age < 0) {
            throw new IllegalArgumentException("年齢は0以上である必要があります");
        }
    }
}

// 使い方
var user1 = new User("Taro", 25); // OK
// var user2 = new User("Jiro", -1); // ここでIllegalArgumentExceptionが発生

public User { ... }のように、引数リストの()を省略して書くのが特徴です。この中でバリデーションロジックを記述します。

フィールドへの代入(this.age = age;など)は、コンパイラが自動で行ってくれるため不要です。

注意
Spring Bootのリクエストボディなどで@NotNull@MinといったBean Validationのアノテーションをrecordのフィールドに付けたい場合は注意が必要です。

単純に引数へアノテーションを書くだけでは効かないことがあり、アノテーション側が @Target(ElementType.RECORD_COMPONENT) を含んでいるか確認する必要があります。

Jakarta Bean Validationの標準アノテーション(jakarta.validation.constraints配下)は仕様レベルでrecordコンポーネントへの制約を想定していますが、古いバージョンのライブラリや自作アノテーションではRECORD_COMPONENTがターゲットに含まれておらず、バリデーションが素通りしてしまう事故が起こり得ます。

導入時は必ず不正な値でテストを書いて実際にエラーが発生するか確認することをおすすめします。

独自のメソッド

もちろん、recordにも通常のクラスと同じように、独自のメソッドを追加できます。

public record Circle(double radius) {
    // 円の面積を計算するメソッド
    public double area() {
        return radius * radius * Math.PI;
    }
}

// 使い方
var circle = new Circle(5.0);
System.out.println("円の面積: " + circle.area()); // 出力: 円の面積: 78.5398...

データを保持するだけでなく、そのデータを使った簡単な計算処理などを持たせることも可能です。

equals・hashCode・toStringをカスタマイズする方法

自動生成されるequals()hashCode()toString()は全フィールドを対象にしますが、パスワードなど一部のフィールドを比較や出力から除外したい場合もあります。

そうしたケースでは、通常のクラスと同じようにメソッドを自分でオーバーライドできます。

public record User(String name, String password) {
    // toString()をオーバーライドしてパスワードを非表示にする
    @Override
    public String toString() {
        return "User[name=" + name + ", password=****]";
    }
}

自動生成される実装で困ったときだけ、必要な部分だけを上書きする。これがrecordで個別メソッドをカスタマイズする基本方針です。

全部を手書きし直す必要はありません。

interfaceを実装するrecordの例

recordは他のクラスを継承できませんが、implementsインターフェースを実装することは可能です。

データの形はrecordで、振る舞いの契約はinterfaceで表現する、というのが現代的な使い方です。

public interface Shape {
    double area();
}

public record Square(double side) implements Shape {
    @Override
    public double area() {
        return side * side;
    }
}

public record Circle(double radius) implements Shape {
    @Override
    public double area() {
        return radius * radius * Math.PI;
    }
}

// 使い方
Shape s = new Square(3.0);
System.out.println(s.area()); // 9.0

この方法を使えば、List<Shape> のように複数のrecordを共通のインターフェース型でまとめて扱えます。

継承のかわりに「契約」で多態性を実現できる、recordと相性の良い設計パターンです。

recordとイミュータブル(不変)な設計の関係

recordを理解する上で非常に重要なのが「イミュータブル(不変)」という概念です。

recordで宣言されたフィールドは、すべてprivate finalとして扱われます。

これは、一度インスタンスを作成したら、その中身(フィールドの値)を後から変更できないことを意味します。setterメソッドも自動生成されません。

あわせて読む

なぜ不変であることが重要なのでしょうか。

不変なオブジェクトは、一度作れば状態が変わらないため、プログラムの動作を予測しやすくなります。

特に、複数のスレッドから同時にアクセスされるような環境(マルチスレッドプログラミング)では、データが意図せず書き換えられる心配がなくなり、非常に安全にデータを扱うことができます。

recordは、この安全で堅牢な「不変オブジェクト」を、非常に簡単に作成するための仕組みなのです。

実際にrecordがコンパイラレベルで何を継承しているかは、次の1行で自分の目で確認できます。

System.out.println(Point.class.getSuperclass());
// 出力: class java.lang.Record

すべてのrecordは暗黙的にjava.lang.Recordを継承したfinalクラスとしてコンパイルされます。

これが「recordは他のクラスを継承できない」「フィールドが必ずfinalになる」という制約の正体です。

手元のIDEやjshellで一度実行しておくと、後述する継承不可の理由も腹落ちしやすくなります。

recordと通常のクラスの違い

recordと通常のクラスの違いを示す比較図解

recordが便利なことは分かりましたが、具体的にclassと何が違うのでしょうか。コード量以外の違いを詳しく見ていきましょう。

classとのコード量の比較

これはすでにお見せしましたが、改めて比較してみます。

【従来のclass(約40行)】

public final class PointClass {
    // フィールド、コンストラクタ、getter、equals、hashCode、toString...
    // (先ほどの長いコード)
}

【record(1行)】

public record PointRecord(int x, int y) {}

コード量は圧倒的にrecordが少なく、クラスの目的が「xyというデータを保持すること」であることが一目瞭然です。可読性と保守性が劇的に向上します。

getter/setterの違い

recordclassでは、フィールドへのアクセス方法が異なります。

  • class (getter): 通常、getという接頭辞がつきます。(例: point.getX()
  • record (アクセサ): getはつかず、フィールド名と同じ名前のメソッドになります。(例: point.x()
あわせて読む

また、前述の通りrecordは不変であるため、値を変更するためのsetterメソッド(setX()など)は生成されません。 これがrecordの大きな特徴の一つです。

equalsやhashCode、toStringの自動生成について

classでは自分で実装する必要があったこれらのメソッドを、recordは自動で、かつ適切に実装してくれます。

  • equals(): 全てのフィールドの値が等しい場合にtrueを返します。
  • hashCode(): 全てのフィールドの値を使ってハッシュ値を計算します。
  • toString(): クラス名と全フィールドの値を"クラス名[フィールド名=値, ...]"という形式の分かりやすい文字列で返します。

これらのメソッドを自分で実装すると、フィールドを追加した際に修正を忘れるなどのミスが起こりがちです。

recordを使えば、そうした人為的なミスを防ぐことができ、コードの安全性が高まります。

配列フィールドを持つrecordの落とし穴

自動生成されるequals()hashCode()は、フィールドがbyte[]int[]などの配列型の場合、要素の中身ではなく配列の参照(メモリ上の位置)だけを比較します。

これはJavaの配列自体がequals()を独自にオーバーライドしていないためで、record固有の弱点ではなく仕様上の制約です。

public record ImageData(String name, byte[] bytes) {}

var a = new ImageData("a.png", new byte[]{1, 2, 3});
var b = new ImageData("a.png", new byte[]{1, 2, 3});

System.out.println(a.equals(b)); // false(中身は同じでも参照が違うため)

DTOに画像バイナリやハッシュ値などの配列フィールドを含める設計は珍しくないため、この挙動を知らずにテストコードでassertEqualsが想定通りに動かず調査に時間を使ってしまうケースを見かけます。

配列を扱うrecordで値の一致を比較したい場合は、equals()hashCode()toString()をオーバーライドしてArrays.equals()Arrays.hashCode()Arrays.toString()を使うか、そもそも配列ではなくList(不変なList.of()など)で持たせる設計に変更するのが実務上の定石です。

Lombok(@Data・@Value)との違いと使い分け

「コード削減ならLombokでもよくない?」とよく質問を受けます。

確かにLombokの@Data@Valueを使えば、getter・equals・toStringを自動生成でき、見た目はrecordと近い結果になります。

ただし、両者は中身がかなり違います。

あわせて読む
record(JDK標準)Lombok @Value
不変性強制(全field final)@Valueなら不変、@Dataなら可変
継承不可可能
追加ツール不要(Java16+)Lombok依存・IDEプラグイン必須
アクセサx() 形式getX() 形式
Builderパターン非対応(手書き要)@Builderで自動生成

使い分けの目安は「不変なデータ保持ならrecord、可変・継承・Builderが要るならLombok」。

Java16以降を採用しているプロジェクトで純粋なDTOを書くなら、外部依存のないrecordを優先するのが筋が良いです。

実務で見ると、Lombokは便利な反面、IDEプラグインの更新タイミング次第でgetter/setter補完が一時的に壊れたり、annotation processorのビルド時間が地味に効いてくる場面があります。

recordなら標準仕様なのでこの種のメンテ負債が乗りません。

なお、record専用のwith構文(Derived Record Creation、通称withers)はJEP 468として提案されていますが、2026年8月時点ではまだドラフトJEPの段階で、正式なJDKリリースにプレビュー機能としても含まれていません。

将来正式化されればBuilderを使わずイミュータブルなコピー生成ができるようになる見込みです。

recordやLombokの背景にある設計思想をもう少し体系的に学びたい方には、以下の書籍がおすすめです。

イミュータブル設計やAPIの作り方の考え方が体系的にまとまっています。

recordを使うメリット・デメリット

recordを使うメリット・デメリットを示す図解

どんな技術にも良い面と悪い面があります。recordを効果的に使うために、メリットとデメリットの両方をきちんと理解しておきましょう。

メリット① コードが短くなる

最大のメリットは、やはりコードの記述量を大幅に削減できる点です。

DTOやVO(Value Object)など、データを保持するだけのクラスを簡潔に書けるため、開発効率が向上します。

コードが短くなることは、可読性の向上にも直結し、結果としてメンテナンスしやすいプログラムに繋がるでしょう。

メリット② 不変データを安全に扱える

recordは、設計上イミュータブル(不変)です。

意図しないデータの書き換えを防げるため、プログラムの堅牢性が上がります。

特にマルチスレッドで複数の処理が並行に動くシステムでは、不変性のメリットが大きく、安心してデータを使い回せます。

私が以前担当した銀行口座系のドメインでは、publicフィールドで残高を持っていたコードを引き継いだ際、別チームのメンバーが残高フィールドを直接書き換えるコードを混入させ、データ不整合の事故が起きたことがあります。

private+getter/setterに統一したあと同種のバグは0件になりました。

recordはそもそもfieldがprivate finalでアクセサ経由しか提供されないので、こうした「うっかり書き換え事故」を構文レベルで封じてくれます。

チームに新人が増えたタイミングほど効きが大きい仕組みです。

デメリット① 継承ができない

recordは、他のクラスをextends(継承)できません。

これは、recordが内部的にjava.lang.Recordという特別なクラスを継承しているためです。

Javaではクラスの多重継承が認められていないため、他のクラスを継承できないという制約があります。

ただし、implementsを使ってインターフェースを実装することは可能です。

もっとも、現場目線で言うと「継承できない」はむしろメリット側に転びがちです。

親クラスが何十段にも積み上がったプロジェクトでは、処理を追うだけで疲弊し、ちょっとした修正でも影響範囲の見極めに時間が溶けます。

recordが継承を禁止しているのは、データクラスを「継承で拡張するな・組み合わせ(コンポジション)で表現しろ」という設計上のガードレールとして機能していると捉えるのが、長期保守の観点では筋が良いです。

あわせて読む
あわせて読む

デメリット② 柔軟な拡張には向かない

recordのフィールドはすべてfinalであり、インスタンス変数(finalではないフィールド)を持つことができません。

そのため、「オブジェクト作成後に状態が変化する」ような、可変(ミュータブル)なクラスとして設計したい場合にはrecordは不向きです。

recordはあくまで「変わらないデータのかたまり」を定義する機能であり、可変クラスを置き換えるものではないと押さえておくと迷いません。

recordの実践例|DTO・APIレスポンス・Spring Bootでの使いどころ

recordのDTO・APIレスポンス・Spring Bootでの実践例を示す図解

recordがどのような場面で特に役立つのか、具体的な実践例を見ていきましょう。

recordを使ったDTO(データ転送オブジェクト)の例

DTOは、異なるレイヤー間(例: サービス層とコントローラー層)でデータをやり取りするために使われるオブジェクトです。

DTOの役割は純粋にデータを運ぶことなので、recordの特性と非常に相性が良いです。

あわせて読む

例えば、ユーザー情報を運ぶUserDtoを考えてみましょう。

【classの場合】

// フィールド、コンストラクタ、getter、equals...などが必要
public class UserDto {
    private final long id;
    private final String name;
    private final String email;
    // ...
}

【recordの場合】

public record UserDto(long id, String name, String email) {}

recordを使えば、このように1行で定義が完了します。

データベースから取得したデータを詰め替えて、APIのレスポンスとして返すような場面で大活躍します。

Spring Bootでrecordを使うケース

WebアプリケーションフレームワークのSpring Bootでは、recordがREST APIのリクエスト/レスポンスDTOとしてそのまま使えます。

Jackson経由のJSON変換も自動で効くため、ボイラープレートが目に見えて減ります。

Spring Boot 3.x系(2026年8月時点の最新系)は、recordをAPIのリクエストボディやレスポンスボディとして自動的に解釈してくれます。

@RestController
public class UserController {

    // ユーザー情報を表現するrecord
    public record UserResponse(long id, String name) {}

    @GetMapping("/users/{id}")
    public UserResponse getUser(@PathVariable long id) {
        // 本来はデータベースなどからユーザー情報を取得する
        // ここではダミーデータを返す
        return new UserResponse(id, "Taro Yamada");
    }
}

このコードでは、UserResponseというrecordを定義し、それをコントローラーメソッドの戻り値にしています。

Spring Bootは、このrecordオブジェクトを自動的に以下のようなJSONに変換してクライアントに返してくれます。

{
  "id": 1,
  "name": "Taro Yamada"
}

recordのおかげで、JSONの構造とJavaのコードが1対1で対応し、非常に見通しが良くなります。

Java 21のレコードパターン(switchでの分解マッチング)

Java 21(2023年9月リリース)で正式化されたレコードパターン(Record Patterns)を使うと、switch式やinstanceofrecordのフィールドを直接分解できます。

これまで p.x()p.y() と何度も呼んでいたコードが、宣言的に書けるようになります。

public sealed interface Shape permits Circle, Square {}
public record Circle(double radius) implements Shape {}
public record Square(double side) implements Shape {}

// Java 21 のレコードパターンで分解
public static double area(Shape s) {
    return switch (s) {
        case Circle(double r) -> r * r * Math.PI;
        case Square(double side) -> side * side;
    };
}

case Circle(double r) のように、recordの中身を直接変数で受け取れます。

sealed interface と組み合わせると、Java公式が想定する「データ指向プログラミング」の形になり、コードの意図が一段クリアになります。

Java 21以上のプロジェクトなら積極的に採用したい機能です。

recordをSerializable(シリアライズ)に対応させる方法

キャッシュへの保存や分散システム間の受け渡しでrecordをシリアライズしたい場合、通常のクラスと同様にSerializableを実装するだけで対応できます。

import java.io.Serializable;

public record UserDto(long id, String name) implements Serializable {}

全フィールドがSerializableを実装した型(またはプリミティブ型)であれば、recordは特別な設定なしでシリアライズ・デシリアライズが可能です。

ただし、recordのシリアライズ形式は「正規コンストラクタ経由での復元」が保証される点が通常のクラスと異なり、独自のシリアライズ処理を書きたい場合はreadObjectではなく前述のコンパクトコンストラクタでバリデーションを行うのがJava公式の推奨です。

recordを使うときの注意点

recordは便利ですが、いくつか注意点もあります。

「recordって結局どこにでも使っていいの?」と聞かれることが多いのですが、答えはNOです。

ここからの3点は特に踏み外しやすいので押さえておいてください。

  1. JPAエンティティには使えない:データベースのテーブルとマッピングするJPAのエンティティとしてrecordを使うことは、2026年8月時点でも推奨されていません。JPAの仕様では、引数なしのコンストラクタやsetterメソッドが要求されることが多く、recordの設計と相性が悪いためです。ただし、JPQLのコンストラクタ式(SELECT NEW)やコンストラクタ射影の戻り値としてrecordを使う、いわゆる「DTOプロジェクション」は、Hibernateを含む主要なJPA実装で正式にサポートされています。@Entityそのものをrecordにするのではなく、取得結果の詰め替え先としてrecordを使う分には問題ありません。エンティティ本体は従来のclassで定義し、それをDTOであるrecordに変換して扱うのが良い方法です。
  2. 可変(ミュータブル)なオブジェクトには使わない:オブジェクトの状態が後から変わることを前提とする設計には、recordを使用してはいけません。例えば、Builderパターンで段階的にオブジェクトを構築していくようなケースや、設定情報を保持しつつ動的に変更するようなオブジェクトには不向きです。
  3. 何でもrecordにしようとしない:recordは銀の弾丸ではありません。データ保持という明確な目的を持つ場合にのみ使用し、複雑なビジネスロジックや状態管理が必要な場合は、これまで通りclassを使いましょう。

よくある質問(FAQ)

Q1. Javaのrecordとclassの違いは何ですか?

A1. recordはデータ保持専用で不変(イミュータブル)、フィールドは自動的にprivate finalになり、getter・equals()hashCode()toString()が自動生成されます。classは可変な状態や継承、複雑なロジックを持てる汎用的な仕組みです。

Q2. recordはいつ使うべきですか?

A2. DTO・VO・APIのリクエスト/レスポンス・設定情報など「データをそのまま保持して運ぶだけ」の場面で使うのが基本です。状態が変化するオブジェクトやJPAエンティティ、継承が必要な設計には向きません。

Q3. recordのgetterはなぜgetXという名前にならないのですか?

A3. recordのアクセサメソッドはフィールド名と同じ名前(例: x())になる仕様です。get接頭辞が付く従来のJavaBeans形式とは異なり、簡潔さを優先した設計になっています。

Q4. Spring BootでrecordをAPIのレスポンスに使えますか?

A4. はい、Spring Boot 3.x系ではrecordをそのままリクエスト/レスポンスボディとして扱え、Jackson経由でJSONへ自動変換されます。Bean Validationのアノテーションを使う場合は@Target(RECORD_COMPONENT)対応の有無を確認してください。

Q5. recordとLombokの@Dataはどちらを使うべきですか?

A5. 不変なデータ保持が目的で外部ライブラリを増やしたくないならrecord、可変なオブジェクトや@Builderによるビルダーパターンが必要ならLombokの@Data@Valueを使うのが目安です。

recordを使うべきか?まとめと判断基準

最後に、recordclassをどのように使い分ければよいのか、判断基準をまとめます。

classとrecordの使い分けポイント

この2つは競合するものではなく、目的によって使い分ける「適材適所」の関係です。

  • record を使うべき時:
    • 目的: データの集合を「不変なコンテナ」としてシンプルに表現したい。
    • 具体例: DTO、VO、APIのレスポンス/リクエスト、設定情報など。
    • キーワード: 不変性、データ、簡潔さ
  • class を使うべき時:
    • 目的: 状態を持ち、その状態が変化する。または、他のクラスとの継承関係や、複雑なビジネスロジックをカプセル化したい。
    • 具体例: サービス、リポジトリ、ドメインオブジェクト(状態変化を伴うもの)、JPAエンティティなど。
    • キーワード: 可変性、振る舞い、拡張性

recordが向いているケース/向かないケース

向いているケース向かないケース
概要主にデータを保持することが目的の場合状態の変化や複雑なロジックが主目的の場合
具体例・DTOやVO ・APIのリクエスト/レスポンス ・複数の値を返すメソッドの戻り値・JPAエンティティ ・継承が必要な設計 ・Builderパターンなど可変性が求められる場面

今後のJava開発でrecordが主流になるのか

recordがすべてのclassを置き換えることはありません。

しかし、データを扱うという特定の領域においては、間違いなくrecordが今後のスタンダードになっていくでしょう。

冗長なコードを減らし、コードの意図を明確にするrecordは、現代のJava開発における強力な武器です。

これまで何気なくclassで書いていたデータ保持用のクラスを、一度「これはrecordで書けないか?」と考えてみる癖をつけるだけで、あなたのJavaコードはよりクリーンで、より安全なものに進化するはずです。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門