たなかのJava日記

どんなことをやったか(学んだか)、どこで詰まったか(わからなかったか)、どこで工夫したかの記録です。

【record】違う種類の値をまとめて扱うレコードとは何が便利か・・・

レコードとListや配列など、値をまとめて扱う仕組みが色々ありますが、簡単にまとめると以下になると思います。

 

■値をまとめて扱う仕組みの分類

Listや配列

⇒同じ種類の値をまとめて扱うことができる

レコード

⇒違う種類の値をまとめて扱うことができる

 

しかし、Listでは違う種類の値を扱うことができないのでしょうか。Java16でレコードが正式採用される前には何らかの形でやりくりしていたと考えられます。その結果を踏まえ、レコードの長所は何なのかを具体例を交えながら解決していけたらいいなと思います。

 

■まずListでは違う種類の値を扱うことができないのかを考える

 

例としてString型の受験者名と科目名、int型の点数をListでまとめて扱ってみます。

 

var exam = List.of("tanaka", "math", 70);

 

最初の項目に「受験者名」、

2番目に「科目名」、

3番目に「点数」を入れています。

 

最初の項目を取得すると受験者名を取得できます。

exam.get(0)

⇒tanaka

 

3番目の項目から点数がわかります。項目番号は0から始まるので3番目の項目を取るにはgetメソッドに2を渡します。

exam.get(2)

⇒70

 

ただListだと以下を自分で覚えておかないといけません。

・get(0)とすると何を得られるのか

・どの要素番号に何が入っているのか

ちょっと面倒ですね。

 

また、Listは文字列と整数の共通部分を抽出した型を扱うことになっています。

もう少し砕いて述べると【文字列と整数を両方扱えるけど文字列でも整数でもない型】になっています。

そのためget(0)で文字列が返ってくるとしてもプログラム上は文字列として扱えません。String型のメソッドを呼び出そうとしてもエラーになります。

 

exam.get(0).toUpperCase()

⇒エラー

 

このエラーの解決方法として、キャストがあります。

Listが扱うことになっているよくわからん型ではなく、実際の値の型に従ってメソッドを呼び出せばいいのです。数値以外のキャストでは値の内容は変わりませんので、プログラム上での扱いだけを変更します。

 

(String)exam.get(0)).toUpperCase()

⇒TANAKA

 

数値が入っている3番目の要素に対して間違えてString型へのキャストを行うと、ClassCastExceptionという例外が発生します。

 

((String) exam.get(2).toUpperCase()

⇒例外発生

 

ここでのキャストは値を扱うための型を変更するものです。しかし、その方では扱えない値を処理しようとすると、このような例外が発生します。

 

詳しくはわかりませんが、とりあえず型としてデータの内容をうまく表現できずに面倒なことになっていることがわかりました。考えないといけないことや踏まないといけない手順が多いです。

 

■レコードで違う種類の値をまとめて扱い、Listと比べてみる

この課題を解決するためには、項目に名前を付けて、型を指定して扱えると良いわけですが、その際に使用を検討できるのがJava 16から正式採用【レコード】です。

 

それでは早速、先ほどの違う種類の3つの値をまとめて扱ってみます。

 

record Exam(String name, String subject, int score) {}

 

【構文】レコードの定義

record レコード名(コンポーネントの型 コンポーネントの名前, ...) {}

 

【レコード名に使える文字】

⇒変数と同じくアルファベットや数字など。数字から始めることはNG。

小文字で始めることも出来ますが、慣習としては大文字で始めます。

 

また、レコードの要素を「コンポーネント」と呼びます。

今回のExamレコードはString型のname、subjectとint型のscoreの3つのコンポーネントを持っています。

 

【レコードのオブジェクトを生成する】

var student = new Exam("tanaka", "math", 70);

コンポーネントの値は、コンポーネントを定義し順で指定します。

 

【レコードオブジェクトからコンポーネントの値を取得する】

student.name()

⇒tanaka

 

student.score()

⇒70

 

■まとめ

Listを使用した場合に比べ、名前を取るにはname()、得点を取るにはscore()とすればよく、コードを見てどんな値を取ってくるのかが格段にわかりやすくなりました。

 

名前を取得する

List・・・get(0)

record・・・name()

 

得点を取得

List・・・get(2)

record・・score()

 

そして、それぞれのコンポーネントに型を指定しているので、そのまま適切なメソッドを呼び出せます。

 

String型のメソッドを呼び出す

student.name().toUpperCase()

⇒TANAKA

 

このように、レコードを使用すると正しくわかりやすいプログラムが作りやすくなることがわかりました。