JavaRush /Java Blog /Random-JA /コーヒーブレイク#230。Java のレコードとは何ですか?またその仕組みは何ですか?

コーヒーブレイク#230。Java のレコードとは何ですか?またその仕組みは何ですか?

Random-JA グループに公開済み
出典: JavaTechOnline この記事では、Java のレコードの概念について、構文、作成方法、使用方法などの例を示して詳しく説明します。 コーヒーブレイク#230。 Java のレコードとは何か、またその仕組み - 1Java のレコードは、最初に Java 14 でプレビュー機能として導入され、最終的に Java 17 リリースで登場した優れた機能の 1 つであり、多くの開発者が積極的に使用しており、膨大な量の定型コードを削減するのに役立ちます。さらに、レコードのおかげで、クラスを不変にするためにコードを 1 行も記述する必要がありません。

Java で Record を使用するのはどのような場合ですか?

アプリケーションの異なるレイヤー間で不変データを渡したい場合は、Record を使用するのが良い選択となります。デフォルトでは、Java のレコードは不変です。つまり、レコードの作成後にプロパティを変更することはできません。結果として、これはエラーを回避し、コードの信頼性を向上させるのに役立ちます。簡単に言えば、Java で Record を使用すると、クラスを自動的に生成できます。

Java のどこで Record を使用できますか?

一般に、不変のプロパティと自動生成されたメソッドを備えた単純なデータ コンテナを宣言する必要があるあらゆる状況でレコードを使用できます。たとえば、レコードが役立つ使用例をいくつか以下に示します。 データ転送オブジェクト(DTO): Record を使用して、データを含む単純なデータ転送オブジェクトを宣言できます。これは、サービス層とデータベース層の間など、異なるアプリケーション層間でデータを転送する場合に便利です。 構成オブジェクト: レコードを使用して、アプリケーションまたはモジュールの構成プロパティのセットを含む構成オブジェクトを宣言できます。これらのオブジェクトは通常、不変のプロパティを持っているため、スレッドセーフで使いやすくなっています。 値オブジェクト。 レコードは、特定の概念またはドメイン モデルを表す値のセットを含む値オブジェクトを宣言するために使用できます。 API レスポンス: REST API を作成する場合、データは通常、JSON または XML の形式で返されます。このような場合、API 応答を表す単純なデータ構造を定義する必要がある場合があります。レコードは、JSON または XML に簡単にシリアル化できる軽量で不変のデータ構造を定義できるため、これに最適です。 テストデータ。単体テストを作成するときは、多くの場合、特定のシナリオを表すテスト データを作成する必要があります。このような場合、テスト データを表す単純なデータ構造を定義する必要がある場合があります。レコードは最小限の定型コードで軽量かつ不変のデータ構造を定義できるため、これには理想的です。 タプルのようなオブジェクト: レコードを使用して、固定数の関連する値を含むタプルのようなオブジェクトを宣言できます。これは、メソッドから複数の値を返す場合、または関連する値のコレクションを操作する場合に便利です。

Javaのレコードとは何ですか?

Java の Record は、データを保存するために設計されたクラスです。これは従来の Java クラスに似ていますが、より軽量で、いくつかの独自の機能を備えています。レコードはデフォルトでは不変です。つまり、レコードの作成後に状態を変更することはできません。そのため、構成パラメータやデータベース クエリから返された値などの不変データの保存に最適です。これは、一連のフィールドまたは変数で構成されるカスタム データ型と、それらのフィールドにアクセスして変更するためのメソッドを作成する方法でもあります。Java での記録を使用すると、開発者が何度も記述しなければならない定型コードの量が減り、データの操作が容易になります。Java でエントリを作成するための構文は次のとおりです。
record Record_Name(Fields....)

Java で Record を作成して使用する方法と例を示します。

Java でプログラムによってレコードを作成して使用する方法を見てみましょう。

レコードの作成

プログラムによるレコードの作成は、Java で通常のクラスを作成することとあまり似ていません。classの代わりに、Recordキーワードを使用します。また、レコード名の括弧内のデータ型を使用してフィールドを宣言する必要があります。Java でエントリを作成する方法を示すコード例を次に示します。
public record Book(String name, double price) { }
この例では、 namepriceの 2 つのフィールドを持つBook というレコードを作成しました。publicキーワードは、このエントリが定義されているパッケージの外部からこのエントリにアクセスできることを示します。

レコードの使用

Java でレコードを使用するには、通常のクラスの場合と同様に、 new キーワードを使用してレコードをインスタンス化します。以下に例を示します。
Book book = new Book( "Core Java" , 324.25);
ここでは、 nameフィールドをCore Javaに設定し、priceフィールドを324.25に設定して、新しいBook レコード インスタンスが作成されます。レコードが作成されると、フィールド自体の名前を使用してそのフィールドにアクセスできるようになります。get メソッドや set メソッドはありません。代わりに、フィールド名がメソッド名になります。
String name = book.name();

double price = book.price();
ここでは、 Bookレコードから取得された名前フィールド価格フィールド の値が表示されます。フィールドに加えて、レコードにはレコードを操作するために使用できるいくつかの組み込みメソッドもあります。たとえば、toString()equals()hashCode() などです。 Java レコードはデフォルトでは不変であることに注意してください。つまり、一度作成されるとその状態は変更できません。 Java でレコードを作成して使用する方法の別の例を見てみましょう。学生に関する情報を保存するためのレコードが必要な場合を考えてみましょう。
public record Student(String name, int age, String subject) {}
これにより、 nameagesubject の3 つのフィールドを持つStudent というレコードが作成されます。このエントリのインスタンスは次のように作成できます。
Student student = new Student("John Smith", 20, "Computer Science");
次に、フィールド名を使用してフィールド値を取得できます。
String name = student.name();
int age = student.age();
String subject= student.subject();

コンパイル後のレコードはどのようになりますか?

Record は単なる特殊なクラスであるため、コンパイラはこれを通常のクラスに変換しますが、いくつかの制限と相違点があります。コンパイル プロセス後にコンパイラがレコード (Java ファイル) をバイトコードに変換すると、生成された.classファイルにはRecordクラスの追加宣言がいくつか含まれます。たとえば、以下はJava コンパイラによって Studentエントリに対して生成されたバイトコードです。
record Student(String name, int age) {   }

.class ファイルへの書き込み (コンパイル後)

javapツールを使用し、コマンド ラインから以下のコマンドを適用すると、以下の変換されたクラスを見つけることもできます。Java コマンド ラインには、クラス ファイルのフィールド、コンストラクター、メソッドに関する情報を表示するために使用できる javapツールが含まれていることに注意することが重要です。>javap 学生の 結論: バイトコードを注意深くチェックすると、いくつかの観察結果が得られるかもしれません。
  • コンパイラはRecordキーワードをclassに置き換えました。
  • コンパイラはクラスFinalを宣言しました。これは、このクラスを拡張できないことを示します。これは、継承できず、本質的に不変であることも意味します。
  • 変換されたクラスはjava.lang.Recordを拡張します。これは、すべてのレコードがjava.langパッケージで定義されたRecordクラスのサブクラスであることを示します。
  • コンパイラはパラメータ化されたコンストラクタを追加します。
  • コンパイラは、toString()hashCode() 、およびquals()メソッドを自動的に生成しました。
  • コンパイラには、フィールドにアクセスするためのメソッドが追加されました。メソッドの命名規則に注意してください。メソッドはフィールド名とまったく同じです。フィールド名の前にgetまたはsetがあってはなりません。

レコード内のフィールド

Java のレコードは、それぞれが異なる名前と型を持つ一連のフィールドを使用して状態を定義します。レコードのフィールドはレコード ヘッダーで宣言されます。例えば:
public record Person(String name, int age) {}
このエントリには、String型のnameint型のageの 2 つのフィールドがあります。レコードのフィールドは暗黙的に最終的なものであり、レコードの作成後に再割り当てすることはできません。新しいフィールドを追加することもできますが、これはお勧めしません。レコードに追加される新しいフィールドは静的である必要があります。例えば:
public record Person(String name, int age) {

   static String sex;
}

レコード内のコンストラクター

従来のクラス コンストラクターと同様、レコード コンストラクターはレコード インスタンスの作成に使用されます。Records には、正規コンストラクターとコンパクト コンストラクターという 2 つのコンストラクターの概念がありますコンパクト コンストラクターは、レコード内の状態変数を初期化するためのより簡潔な方法を提供します。一方、正規コンストラクターは、より柔軟性のあるより伝統的な方法を提供します。

正規コンストラクター

デフォルトの Java コンパイラは、引数を対応するフィールドに割り当てる全引数コンストラクター (全フィールド コンストラクター) を提供します。これは正規コンストラクターとして知られています。データを検証するための条件ステートメントなどのビジネス ロジックを追加することもできます。以下に例を示します。
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

コンパクトデザイナー

コンパクト コンストラクターは、括弧を含むすべての引数を無視します。対応するフィールドは自動的に割り当てられます。たとえば、次のコードは、Recordsのコンパクト コンストラクターの概念を示しています。
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
コンパクト コンストラクターに加えて、通常のクラスと同様に、Recordで通常のコンストラクターを定義できます。ただし、すべてのコンストラクターがレコードのすべてのフィールドを初期化することを確認する必要があります。

レコード内のメソッド

Java のレコードは、レコード内のフィールドごとに一連のメソッドを自動的に生成します。これらのメソッドはアクセサー メソッドと呼ばれ、関連付けられているフィールドと同じ名前が付けられます。たとえば、レコードにPriceという名前のフィールドがある場合、そのレコードには、 Priceフィールドの値を返すPrice()というメソッドが自動的に追加されます。自動的に生成されるアクセサー メソッドに加えて、通常のクラスと同様に、エントリ内で独自のメソッドを定義することもできます。例えば:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
このエントリには、nameフィールドを使用して挨拶を出力するSayHello()メソッドがあります。

Record が定型コードの削減にどのように役立つか

Java 記法が定型コードの排除にどのように役立つかを簡単な例で見てみましょう。名前、年齢、電子メール アドレスを持つ人を表すpersonというクラスがあるとします。これが従来の方法でクラスを定義する方法です。 Recordを使用しないコード:
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
ご覧のとおり、このクラスでは、フィールド、コンストラクター、ゲッター、セッター、およびtoString()メソッドを定義するために多くの定型コードが必要です。さらに、このクラスを不変にしたいとします。これを行うには、次のような追加の手順を実行する必要があります。
  • クラスを拡張できないように、クラスをFinalとして宣言します。
  • すべてのフィールドをprivateおよびFinalとして宣言し、コンストラクターの外部で変更できないようにします。
  • フィールドにはセッターメソッドを提供しないでください。
  • いずれかのフィールドが変更可能な場合は、元のオブジェクトを返すのではなく、そのコピーを返す必要があります。
Recordを使用したコード:
public record Person(String name, int age, String email) {}
それだけです!たった 1 行のコードで、従来のクラスと同じフィールド、コンストラクター、ゲッター、セッター、およびtoString()メソッドを持つクラスを定義しました。Record構文は、すべての定型コードを処理します。上記の例から、Java でレコードを使用すると、固定のフィールド セットを持つクラスを定義するためのより簡潔な構文が提供されるため、定型コードの排除に役立つことが明らかです。従来のアプローチよりもレコードの定義と使用に必要なコードが少なくなり、レコードはフィールドを更新するメソッドを使用してフィールドに直接アクセスできるようになります。これにより、コードが読みやすく、保守しやすくなり、エラーが発生しにくくなります。さらに、Record構文を使用すると、クラスを不変にするために特別な操作を行う必要がありません。デフォルトでは、レコード内のすべてのフィールドはFinalであり、レコード クラス自体は不変です。したがって、書き込み構文を使用して不変クラスを作成することは、従来のアプローチよりもはるかに簡単で、必要なコードも少なくなります。レコードを使用する場合、フィールドをFinalとして宣言したり、フィールドを初期化するコンストラクターを提供したり、すべてのフィールドにゲッターを提供したりする 必要はありません。

共通レコードクラス

Java では、汎用Recordsクラスを定義することもできます。ジェネリック エントリ クラスは、1 つ以上の型パラメータを持つエントリ クラスです。以下に例を示します。
public record Pair<T, U>(T first, U second) {}
この例では、 Pair はタイプTUの 2 つのパラメーターを取る汎用レコード クラスです。このレコードのインスタンスは次のように作成できます。
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Record 内のネストされたクラス

エントリ内でネストされたクラスとインターフェイスを定義することもできます。これは、関連するクラスとインターフェイスをグループ化するのに役立ち、コードベースの編成と保守性の向上に役立ちます。以下は、ネストされたクラスを含むエントリの例です。
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
この例では、Person は、ネストされたContactクラスを含むエントリです。一方、Contact は、電子メール アドレスと電話番号という 2 つのプライベート最終フィールドを含む静的な入れ子クラスです。また、電子メールと電話番号を受け入れるコンストラクターと、getEmail()getPhone()という 2 つのゲッター メソッドもあります。次のようにPersonインスタンスを作成できます。
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
この例では、 Johnという名前の 新しいPersonオブジェクト (年齢 30 歳) と、電子メール john@example.com と電話番号123-456-7890の新しいContactオブジェクトを作成しました。

Record 内のネストされたインターフェイス

以下は、ネストされたインターフェースを含むエントリーの例です。
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
この例では、Book はネストされたEditionインターフェイスを含むエントリです。つまり、Edition は、単一のgetName()メソッドを定義するインターフェースです。次のようにBookインスタンスを作成できます。
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
この例では、ダグラス アダムス『銀河ヒッチハイク ガイド』というタイトルの新しいBookオブジェクトと、 getName()メソッドが呼び出されたときにScience Fiction という名前を返すEditionインターフェイスの新しい匿名実装を作成します。

Records では他に何ができるでしょうか?

  • エントリではカスタム コンストラクターを定義できます。レコードはパラメーター化されたコンストラクターをサポートしており、本体に指定されたパラメーターを使用してデフォルトのコンストラクターを呼び出すことができます。さらに、レコードはコンパクト コンストラクターもサポートしています。これはデフォルトのコンストラクターに似ていますが、コンストラクターの本体にチェックなどの追加機能を含めることができます。
  • Java の他のクラスと同様に、Record はインスタンス メソッドを定義して使用できます。これは、記録クラスに固有のメソッドを作成して呼び出すことができることを意味します。
  • Recordでは、インスタンス変数はコンストラクター パラメーターとしてのみ指定できるため、クラス メンバーとして定義することはできません。ただし、レコードは静的フィールドと静的メソッドをサポートしており、これらを使用して、レコード クラスのすべてのインスタンスに共通のデータを保存したりアクセスしたりできます。

レコードはインターフェイスを実装できますか?

はい、Java で記述するとインターフェイスを実装できます。たとえば、以下のコードはその概念を示しています。
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
ここでは、単一のprint()メソッドを使用してPrintableインターフェイス を定義しました。また、Printableインターフェイスを実装するPersonエントリも定義しました。人物レコードには、 nameageの 2 つのフィールドがあり、Printableインターフェイスの print メソッドをオーバーライドして、これらのフィールドの値を印刷します。次のように Personエントリをインスタンス化し、その print メソッドを呼び出すことができます。
Person person = new Person("John", 30);
person.print();
これにより、名前: John、年齢: 30 がコンソールに 出力されます。この例に示すように、Java レコードは通常のクラスと同じようにインターフェイスを実装できます。これは、エントリに動作を追加したり、インターフェイスによって定義されたコントラクトにエントリが準拠していることを確認したりする場合に役立ちます。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION