目次
- はじめに
- ジェネリクスってなーに
- 基本構文
- メリット
- 使用例
- まとめ
はじめに
MapをListで管理したくてググっていたら、ジェネリクスと呼ばれるものが出てきて感動したのでまとめてみました
※バージョンは17を使用しております
ジェネリクスってなーに
・型安全性を高めて、実行時エラーを防いでくれる素晴らしい機能
・クラス、インターフェースやlist、map等のコレクションフレームワークなどで使用できる
・ListやMap等に格納するデータ型を明示的に指定することができる
例で理解するジェネリクス
物を片付けるために箱を使うとき、何を入れるかのルールを決めておかないと、箱の中身が混ざってしまい、何が入っているかがわからなくなります。そうすると、後で物を使うときに困ってしまいます。
そこで、箱ごとに何を入れるかを決めて整理すると、間違った箱に物を入れたときに気づくことができ、物をきちんと管理することができます。
このように、何を入れるかを決める行為は、プログラミングで「データ型を指定する」ことに似ており、ジェネリクス(例えば、ListやMap)を使って、どんなデータを格納できるかを制限することと同じです。
箱に何を入れていい物を決める行為が、データ型を明示的に指定してListやMapに格納できる値を制限するジェネリクスに該当します。
ジェネリクスの基本構文
明示的に格納するデータ型を指定することで、指定した型以外の型の値が格納されないようにすることができる。
以下はダイヤモンド演算子を使用して、Listに格納できるデータ型をString型に限定している。
// ダイヤモンド演算子<>を使用する
// 例
List<String> stringList = new ArrayList<>();
※プリミティブ型(intやdouble)は直接使用することができないため、注意が必要
// コンパイルエラー
List<int> intList = new ArrayList<>(); // エラー
// 正しい方法
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
ジェネリクスを使用するメリット
1.型安全性の向上
コンパイル時に型の一致を確認できるため、ランタイムエラーを未然に防ぐことができ、キャストによるエラーを減少させることが可能
2.可読性・保守性の向上
型パラメータを明示的に指定することで、コードの意図が明確になるため理解しやすくなる
3.実行時エラーの削減
ジェネリクスは実行時エラーをコンパイルエラーに変えるため、事前にミスを検知しやすくなる
ジェネリクスの使用例
listを使用した参考プログラム
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
// ジェネリクスを使用
List<String> stringList = new ArrayList<>();
// ジェネリクス未使用
List list = new ArrayList();
// 要素の追加
stringList.add("Apple");
stringList.add("Banana");
// stringList.add(123); // StringにIntegerをaddしているため、エラーになる
list.add("Apple"); // String
list.add(123); // Integer
// ジェネリクスでstring型を指定したのでキャスト不要
String fruit1 = stringList.get(0);
String fruit2 = stringList.get(1);
// String fruit123 = stringList.get(2);
// キャストが必要
String fruit3 = (String) list.get(0);
String fruit4 = (String) list.get(1); // 実行時エラー: ClassCastException
// 出力
System.out.println("Fruit 1: " + fruit1);
System.out.println("Fruit 2: " + fruit2);
// System.out.println("Fruit 123: " + fruit123);
System.out.println("Fruit 3: " + fruit3);
System.out.println("Fruit 4: " + fruit4);
}
}
実行結果
Integerをstringにキャストすることはできないが、キャストするようにコードを書いてもコンパイル時にエラーが発生せず、実行時エラーが発生する
(base) test % javac Main.java # コンパイル
ノート: Main.javaの操作は、未チェックまたは安全ではありません。
ノート: 詳細は、-Xlint:uncheckedオプションを指定して再コンパイルしてください。
(base) test % java Main # 実行
Fruit 1: Apple
Fruit 2: Banana
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at Main.main(Main.java:31)
私はMybatysを用いてデータベースから取得したMapをListで管理したかったので、以下のようにListとMapを組み合わせて使用しました
List<Map<String, Object>> thisMonthLearningTimeList = learningDataMapper.getTotalLearningTimeByCategoryType(
dbUser.getUserId(), years.get(0), months.get(0));
まとめ
- 型安全性の向上:コンパイル時に型チェックが行われ、実行時エラーを防げる
- コードの可読性とメンテナンス性の向上:明確な型指定により、コードの意図が伝わりやすくなる
- より詳しく知りたい方は、Oracleの以下のリンクから公式ドキュメントをご参照ください
- https://www.oracle.com/jp/java/technologies/documentation.html