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

Java入門

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

CATEGORY
REV.
DRAWN BY
TOM
SHEETS
7枚
record(classとの違いと使いどころ)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

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

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

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

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

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

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

record(record型・レコードクラスとも呼ばれます)は、Java 16で正式採用された不変データ専用のクラスです。classとの違いは「不変・継承不可・アクセサ名がx()形式」の3点に集約され、DTOやAPIのレスポンスなら迷わずrecordを選べます。

x・y座標だけを持つ「点」のクラスを、従来の書き方だと以下のように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) {}

基本構文・classとの違いの比較表・コンパクトコンストラクタ・Spring Bootでの実践例・使うべきケースと避けるべきケースを、2026年時点の仕様で順に整理しました。

とくに次のような方に役立つ内容です。

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

使い分けに迷ったときは、記事後半の判断フロー図から確認できます。

結論

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

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

PARTS LIST /目次

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

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

日本語の記事では「record型」「レコードクラス」とも呼ばれます。Java言語仕様(JLS)での正式名称は「Record Classes」で、どの呼び方も同じ機能を指しています。

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

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()メソッド(デバッグ用の文字列出力)

この6つをすべて手で書く必要がありました。どれも決まりきった作業なのに、フィールド追加時の修正漏れなどバグの原因になりやすく、開発者の負担でした。

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

recordの基本構文と定義方法

同じPointクラスをrecordで定義すると、次の1行になります。

public record Point(int x, int y) {}
たったこれだけです。

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

この1行で、冒頭の40行近いコードとまったく同じ機能を持つクラスを定義できます。

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

record

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

書く量が減るだけでなく、フィールドを増やしても生成漏れ・修正漏れが起きない点もrecordの強みです。

recordが自動で用意してくれるもの

RECORD / 自分で書くのはこの1行

record Point(int x, int y) { }

↓ この1行から自動生成される

GENERATED / 自動で用意されるもの

1Point(int x, int y)コンストラクタ
2x() と y()アクセサ
3equals() / hashCode()値の比較用
4toString() → Point[x=1, y=2]文字列にする
NOTEフィールドは不変(final)。継承はできない。

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

インスタンスの生成・コンパクトコンストラクタ・独自メソッド・interface実装の4つを押さえれば、実務で使う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のフィールドに付けたい場合は注意が必要です。

recordのコンポーネントに付けたアノテーションは、そのアノテーションの@Targetが許す要素(フィールド・アクセサメソッド・コンストラクタ引数)へ引き継がれます(JLS 8.10.3)。

Jakarta Bean Validationの標準アノテーション(jakarta.validation.constraints配下)は@TargetにFIELD・METHOD・PARAMETERを含むため、recordのコンポーネントにそのまま付けて動きます。確認が必要なのは自作アノテーションで、@Targetにこれらが入っておらずRECORD_COMPONENTだけだと、フィールドやアクセサに引き継がれず検証されません。Spring MVCでは@RequestBodyの引数に@Validも必要です。

導入時は不正な値でテストを書いて、実際にエラーが発生するか確認してください。

独自のメソッド

もちろん、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メソッドも自動生成されません。ただし不変なのはフィールドの参照までです(浅い不変)。ListやMap、配列のような可変オブジェクトをコンポーネントに持たせると、その中身は変更できてしまうため、コンパクトコンストラクタでList.copyOf()などによる防御的コピーを行います。

あわせて読む

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

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

とくに複数のスレッドから同時にアクセスされる環境(マルチスレッドプログラミング)では、データが意図せず書き換えられる心配がありません。

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

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

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

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

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

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

recordとclassの違い【比較表で一覧】

recordとclassの違いは、次の8項目に整理できます。

項目recordclass
定義の書き方record Point(int x, int y) {}の1行フィールド・コンストラクタ等を手書き
フィールド自動でprivate final(不変)自由(可変にできる)
setter生成されない必要なら手書き
アクセサ名x()形式慣例でgetX()形式
equals・hashCode・toString全フィールドで自動生成手書き(またはIDE生成)
継承(extends)不可(暗黙的にfinal)可能
interface実装可能可能
主な用途DTO・VO・APIのリクエスト/レスポンスサービス・JPAエンティティ・状態を持つオブジェクト

ここからは、表の内容のうち迷いやすい2つの違いと、よく比較されるLombokとの違いを順に掘り下げます。

getter/setterの違い

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

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

recordは不変なので、値を変更するsetterメソッド(setX()など)は生成されません。

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で値の一致を比較したい場合の定石は2つです。

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なら可変
継承不可@Dataは可能、@Valueはクラスがデフォルトでfinalのため不可(@NonFinalで外せる)
追加ツール不要(Java16+)Lombok依存・IDEプラグイン必須
アクセサx() 形式getX() 形式
Builderパターン標準では非対応(手書きするか、Lombokの@Builderをrecordに付ける)@Builderで自動生成

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

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

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

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

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

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

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

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

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

recordのメリットは2つ、デメリットも2つです。デメリットは「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のインスタンスフィールドは、ヘッダーで宣言したコンポーネントに対応するprivate finalのものだけです。int y;のような可変フィールドはもちろん、finalを付けた追加のインスタンスフィールドも宣言できません(staticフィールドは宣言できます)。

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

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

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

実務でrecordが活躍するのは、DTO・Spring BootのAPI・switchでの分解・シリアライズの4場面です。

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系・4.x系(2026年10月時点の最新は4.1系)は、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式やinstanceofでrecordのフィールドを直接分解できます。

これまで 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のシリアライズ形式は「正規コンストラクタ経由での復元」が保証される点が通常のクラスと異なります。recordではwriteObject・readObject・writeExternal・readExternalを定義しても無視され、シリアライズ形式はコンポーネントだけで決まります。復元時は正規コンストラクタが呼ばれるため、値の検証は前述のコンパクトコンストラクタで行います。

recordを使うときの注意点

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

トム
トム

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

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

  1. JPAエンティティには使えない:データベースのテーブルとマッピングするJPAのエンティティとしてrecordを使うことは、2026年8月時点でも推奨されていません。JPAの仕様では、エンティティクラスに引数なしコンストラクタがあること、クラスやフィールドがfinalでないことが求められますが、recordは暗黙的にfinalでフィールドもfinalのため使えないからです。ただし、JPQLのコンストラクタ式(SELECT NEW)やコンストラクタ射影の戻り値としてrecordを使う、いわゆる「DTOプロジェクション」は、Hibernateを含む主要なJPA実装で正式にサポートされています。@Entityそのものをrecordにするのではなく、取得結果の詰め替え先としてrecordを使う分には問題ありません。エンティティ本体は従来のclassで定義し、それをDTOであるrecordに変換して扱うのが良い方法です。
  2. 可変(ミュータブル)なオブジェクトには使わない:オブジェクトの状態が後から変わることを前提とする設計には、recordを使用してはいけません。例えば、生成後にフィールドを段階的に書き換えて組み立てていくようなケース(Builderで組み立てた最終結果をrecordにするのは問題ありません)や、設定情報を保持しつつ動的に変更するようなオブジェクトには不向きです。
  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系・4.x系ではrecordをそのままリクエスト/レスポンスボディとして扱え、Jackson経由でJSONへ自動変換されます。Bean Validationのアノテーションを使う場合は、コンポーネントに付けるだけで有効になりますが、@RequestBody の引数に @Valid を付けるのを忘れないでください。自作の制約アノテーションは@TargetにFIELD・METHOD・PARAMETERを含めてください。

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

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

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

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

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

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

作ろうとしているクラスで迷ったら、次の順に質問に答えてみてください。

recordとclassの判断フロー(Q1から順に答える)
QUESTION / 質問
はい
いいえ
Q1. JPAの@Entityとしてテーブルにマッピングする?JPAは引数なしコンストラクタを求め、final のクラス・フィールドも認めない
class を使う検索結果を受け取るDTOプロジェクションならrecordも使える
Q2へ
Q2. 作成後にフィールドの値を変更する必要がある?recordのフィールドはすべてfinalで、setterも生成されない
class を使う状態が変わるオブジェクトはclassで
Q3へ
Q3. 他のクラスを継承(extends)する必要がある?recordは暗黙的にjava.lang.Recordを継承済みなので、extendsできない
class を使う共通の振る舞いだけならinterfaceのimplementsで足りないか確認する
Q4へ
Q4. 主な役割はデータを保持して運ぶこと?DTO・VO・APIのリクエスト/レスポンスなど
record を使う
class を使う状態管理や複雑なビジネスロジックが主役なら
NOTE例:APIレスポンスのDTOは Q1〜Q3 がすべて「いいえ」、Q4 が「はい」で record。JPAエンティティは Q1、後から値を変える設定は Q2、親クラスを継承したいものは Q3 で class に決まります。

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

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

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

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

しかし、データを扱う領域では、recordがスタンダードになりつつあります。

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

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

REVISIONS / 改訂履歴
REV.DATE内容
52026-10-19同じ説明の繰り返しを減らし、読みやすく整えた
42026-10-15図解を描き直し(図面のデザインにそろえた)
32026-08-222026年8月時点の情報で全体を見直し
22026-04-252026年4月時点の情報で全体を見直し
12025-10-12初版
NEXT READ本棚から1本

AIで開発を組み直すなら、まずこの5冊から

入門・実践・設計まで、Claude Codeの本をレベル別に5冊選んで比べました。

→ おすすめ5冊を見る

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

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

Java入門