レコード (Java 16+)
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 51/87。
シンプルなデータ保持クラスを作成する際、多くの場合、プライベートフィールド、コンストラクタ、ゲッター、toString()、equals()、hashCode()といった、繰り返しのボイラープレートコードを記述することになります。Java 16では、この退屈な作業を排除するためにレコードが導入されました。
レコードは、不変(イミュータブル)なデータを保持するために設計された特別なクラスです。recordキーワードの後にコンポーネントリストを続けて宣言します:
record Person(String name, int age) { }
// この1行だけで、自動的に以下が提供されます:
// - private finalフィールド: name と age
// - コンストラクタ: Person(String name, int age)
// - アクセサメソッド: name() と age()
// - toString()、equals()、および hashCode()レコードの使用は簡単です:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]アクセサメソッドは get プレフィックスを使用せず、単にフィールド名と同じ名前になることに注意してください。レコードは暗黙的に final であるため、継承することはできません。すべてのフィールドも final であり、レコードは本質的に不変(イミュータブル)になります。
必要に応じて、カスタムメソッドを追加したり、自動生成されたメソッドをオーバーライドしたりできます。また、コンパクトコンストラクタ内でバリデーションを追加することもできます:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}レコードは、DTO、APIレスポンス、またはボイラープレートコードを書くことなく、シンプルで不変なデータコンテナを必要とするあらゆるシナリオに最適です。
チャレンジ
簡単Javaのrecord(レコード)の優雅さを示す映画チケット予約システムを構築しましょう。レコードによってボイラープレートコードが排除されつつ、カスタムメソッドやバリデーションを追加できる様子を確認できます。
コードは3つのファイルに分けて構成します:
Movie.java: 3つのコンポーネント、title(String)、genre(String)、durationMinutes(int)を持つ、映画を表すレコードを作成します。時間を[hours]h [minutes]mという分かりやすい形式(例:142分は2h 22m)で返すgetFormattedDuration()というカスタムメソッドを追加してください。時間の計算には整数の除算と剰余(モジュロ)を使用します。Ticket.java: 3つのコンポーネント、movie(Movie)、seatNumber(String)、price(double)を持つ、映画チケットを表すレコードを作成します。価格を検証するコンパクトコンストラクタを追加してください。もし価格が0以下であれば、IllegalArgumentExceptionをPrice must be positiveというメッセージとともにスローします。また、以下のマルチライン文字列を返すprintTicket()というカスタムメソッドを追加してください:========== TICKET ========== Movie: [movie title] Genre: [movie genre] Duration: [formatted duration from movie] Seat: [seatNumber] Price: $[price] ============================Main.java: 予約システムを完成させましょう!5つの入力(映画のタイトル (String)、ジャンル (String)、上映時間(分) (int)、座席番号 (String)、チケット価格 (double))を受け取ります。最初の3つの入力で Movie レコードを作成し、次にその movie、座席番号、価格を使用して Ticket レコードを作成します。作成したチケットの
printTicket()を呼び出した結果を出力してください。
入力は、映画のタイトル、ジャンル、上映時間(分)、座席番号、価格の順に5つ与えられます。
従来のクラスと比較して、レコードがいかにクリーンであるかに注目してください。コンストラクタ、ゲッター、toString()、equals()、hashCode() を書く必要はありません。コンパクトコンストラクタを使用すると、フィールドへの代入を繰り返すことなくバリデーションを追加でき、必要に応じてカスタムメソッドを追加することも可能です!
チートシート
Java 16では、単純なデータを保持するクラスのボイラープレートコードを排除するためにレコードが導入されました。レコードは、不変(イミュータブル)なデータを保持するために設計された特別なクラスです。
recordキーワードの後にコンポーネントリストを続けてレコードを宣言します:
record Person(String name, int age) { }これにより、以下が自動的に提供されます:
- private finalフィールド
- コンストラクタ
- アクセサメソッド(
getプレフィックスなし) toString()、equals()、およびhashCode()
レコードの使用例:
Person p = new Person("Alice", 30);
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 30
System.out.println(p); // Person[name=Alice, age=30]レコードは暗黙的にfinalであり、継承することはできません。すべてのフィールドはfinalであり、レコードは本質的に不変です。
コンパクトコンストラクタを使用してバリデーションを追加します:
record Person(String name, int age) {
public Person {
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
}
}レコードにカスタムメソッドを追加できます:
record Person(String name, int age) {
public String getInfo() {
return name + " is " + age + " years old";
}
}自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力を読み込む
String title = scanner.nextLine();
String genre = scanner.nextLine();
int durationMinutes = Integer.parseInt(scanner.nextLine());
String seatNumber = scanner.nextLine();
double price = Double.parseDouble(scanner.nextLine());
// TODO: title、genre、durationMinutes を使用して Movie レコードを作成する
// TODO: movie、seatNumber、price を使用して Ticket レコードを作成する
// TODO: printTicket() メソッドを使用してチケットを出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造