0Pricing
Java Academy · レッスン

レコード入門

簡潔な不変データ保持型としてレコードを宣言し、自動生成されるメンバーを理解します。

「レコード入門」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

レコードの概要

Javaのレコード(Java 16以降)は、不変のデータキャリアクラスを簡潔に宣言する方法です。コンストラクタ、アクセサ、equals、hashCode、toStringといった定型コードを自動生成します。

レコードの宣言

recordキーワードに続けてレコード名とコンポーネントリストを記述します。各コンポーネントは、private finalフィールドとpublicアクセサメソッドになります。

record Point(double x, double y) {}

Point p = new Point(3.0, 4.0);
System.out.println(p.x());      // 3.0 (accessor, not getX())
System.out.println(p.y());      // 4.0
System.out.println(p);          // Point[x=3.0, y=4.0]
System.out.println(p.equals(new Point(3.0, 4.0))); // true

自動生成されるメンバー

コンパイラは、すべてのレコードについて、正規コンストラクタ、publicアクセサ、equals()、hashCode()、toString()を自動生成します。これらを手動で記述する必要はありません。

record Product(String name, double price, int stock) {}

Product p1 = new Product("Widget", 9.99, 100);
Product p2 = new Product("Widget", 9.99, 100);

System.out.println(p1.name());          // Widget
System.out.println(p1.price());         // 9.99
System.out.println(p1.equals(p2));      // true (structural)
System.out.println(p1.hashCode() == p2.hashCode()); // true
System.out.println(p1);                 // Product[name=Widget, price=9.99, stock=100]

レコードは不変

レコードのコンポーネントはfinalです。構築後に変更することはできず、レコードは不変の値オブジェクトを表します。

record Money(long cents, String currency) {}

Money price = new Money(999, "USD");
// price.cents = 500; // Compile error — final field!

// To "update" a record, create a new one
Money discounted = new Money(price.cents() - 100, price.currency());
System.out.println(discounted); // Money[cents=899, currency=USD]

レコードはObjectだけを継承

レコードは暗黙的にjava.lang.Recordを継承し、ほかのクラスを継承することはできません。インターフェースは実装できます。

interface Describable { String describe(); }

record Circle(double radius) implements Describable {
    public String describe() {
        return String.format("Circle with radius %.1f", radius);
    }

    public double area() {
        return Math.PI * radius * radius;
    }
}

Circle c = new Circle(5.0);
System.out.println(c.describe()); // Circle with radius 5.0
System.out.printf("Area: %.2f%n", c.area()); // Area: 78.54

コレクションでのレコード

レコードはすべてのコンポーネントに基づいてequalsとhashCodeを正しく実装するため、MapのキーやSetの要素として正しく機能します。

import java.util.*;

record Coordinate(int row, int col) {}

Map<Coordinate, String> grid = new HashMap<>();
grid.put(new Coordinate(0, 0), "Origin");
grid.put(new Coordinate(1, 2), "Target");

// Lookup with equal-valued record works correctly
System.out.println(grid.get(new Coordinate(0, 0))); // Origin
System.out.println(grid.get(new Coordinate(1, 2))); // Target

Set<Coordinate> visited = new HashSet<>();
visited.add(new Coordinate(3, 3));
System.out.println(visited.contains(new Coordinate(3, 3))); // true

DTOとしてのレコード

レコードはREST APIのデータ転送オブジェクト(DTO)に最適です。振る舞いを持たず、レイヤー間でデータを受け渡します。

record CreateUserRequest(String email, String displayName, String plan) {}

record UserResponse(long id, String email, String displayName,
                    String plan, String createdAt) {}

// Simulating a service call
static UserResponse createUser(CreateUserRequest req) {
    long id = 42L;
    return new UserResponse(id, req.email(), req.displayName(),
        req.plan(), java.time.Instant.now().toString());
}

CreateUserRequest req = new CreateUserRequest("alice@example.com", "Alice", "PRO");
UserResponse resp = createUser(req);
System.out.println(resp);

レコードの分解

レコードはパターンマッチングと相性がよく、switch式の中で分解できます(Java 21以降)。

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}

static double area(Shape s) {
    return switch (s) {
        case Circle(double r)            -> Math.PI * r * r;
        case Rectangle(double w, double h) -> w * h;
        case Triangle(double b, double h) -> 0.5 * b * h;
    };
}

System.out.printf("%.2f%n", area(new Circle(5)));         // 78.54
System.out.printf("%.2f%n", area(new Rectangle(4, 6)));   // 24.00

レコードのシリアライズ

レコードは、そうするように宣言すれば自然にSerializableを実装できます。JSONライブラリ(Jackson、Gson)とも、追加設定なしで適切に連携できます。

// With Jackson (add @JsonProperty if needed)
import com.fasterxml.jackson.databind.ObjectMapper;

record OrderSummary(String orderId, double total, String status) {}

ObjectMapper mapper = new ObjectMapper();
OrderSummary order = new OrderSummary("ORD-001", 149.99, "SHIPPED");

String json = mapper.writeValueAsString(order);
System.out.println(json);
// {"orderId":"ORD-001","total":149.99,"status":"SHIPPED"}

OrderSummary restored = mapper.readValue(json, OrderSummary.class);
System.out.println(order.equals(restored)); // true

ネストしたレコード

レコードは他のレコードをコンポーネントとして持つことができ、豊かな不変オブジェクトグラフを構築できます。

record Address(String street, String city, String country) {}
record Customer(long id, String name, Address address) {}

Address addr = new Address("123 Main St", "New York", "US");
Customer customer = new Customer(1L, "Alice", addr);

System.out.println(customer.name());              // Alice
System.out.println(customer.address().city());   // New York
System.out.println(customer);                    // Customer[id=1, name=Alice, address=Address[street=123 Main St, city=New York, country=US]]

レコードの制限

レコードには、知っておくべきいくつかの制限があります:

  • 他のクラスを継承できません(暗黙的にRecordを継承します)
  • コンポーネントは常にfinalで、可変状態を持てません
  • コンポーネントリストの外にインスタンスフィールドを宣言できません
  • abstractにはできません
// Records CAN have static fields and methods
record Temperature(double celsius) {
    static final double ABSOLUTE_ZERO = -273.15;

    public double toFahrenheit() {
        return celsius * 9.0/5.0 + 32;
    }

    public static Temperature fromFahrenheit(double f) {
        return new Temperature((f - 32) * 5.0/9.0);
    }
}
System.out.println(Temperature.fromFahrenheit(98.6)); // Temperature[celsius=37.0]

クイックチェック

Javaコンパイラは、レコードにどのメンバーを自動生成しますか。

復習:レコードの概要

重要なポイント:

  • レコードは、最小限の定型コードで不変のデータキャリアを宣言できます
  • 正規コンストラクタ、アクセサ、equals、hashCode、toStringが自動生成されます
  • コンポーネントはfinalであり、レコードは設計上不変です
  • レコードはインターフェースを実装できますが、クラスを継承することはできません
  • DTO、値オブジェクト、APIレスポンスに最適です
  • 構造的等価性により、HashMapのキーやHashSetの要素として正しく機能します

よくある質問

「レコード入門」レッスンは無料ですか?

はい。「レコード入門」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「レコード入門」で何を学びますか?

簡潔な不変データ保持型としてレコードを宣言し、自動生成されるメンバーを理解します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「レコード入門」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. レコード入門
  2. コンパクトコンストラクターと検証
  3. レコードのカスタムメソッド
  4. レコード、クラス、Lombok の比較
← Java Academyに戻る