ジェネリクス: 型安全
<T> で型を抽象化。キャストを排除。
なぜジェネリクスが必要か?
ジェネリクスは、型をパラメータ化して再利用性を高める仕組みです。例えば、List<String>、List<Integer>、List<User> など、同じロジックを別の型で使いたいとき、ジェネリクスを使うとコードの重複を防げます。また、キャスト((String) list.get(0))のような危険な操作を排除し、コンパイル時に型チェックができるため、バグを未然に防げます。
いつ使うか?
ジェネリクスは、以下のような場面で使います: 1. **型に依存しないロジックを書きたいとき**: 同じロジックを複数の型で再利用したいとき。 2. **コレクションフレームワークを作りたいとき**: List、Map、Setなどのコレクションを型安全に扱いたいとき。 3. **キャストを排除したいとき**: 実行時のClassCastExceptionを防ぎたいとき。 4. **ユーティリティメソッドを作りたいとき**: Collections.sort()やCollections.max()のような汎用的なメソッドを作りたいとき。
実践テクニック
PECSを覚える
PECS(Producer Extends, Consumer Super)は、ジェネリクスの型パラメータを制約するためのルールです。**Producer Extends T** は「Tまたはそのサブタイプ」を意味し、データを書き込むときに使います。**Consumer Super T** は「Tまたはそのスーパータイプ」を意味し、データを読み出すときに使います。これを覚えると、より柔軟なジェネリクスAPIを設計できます。
型消去を活用する
Javaのジェネリクスは「型消去」という仕組みで、実行時には型情報が失われます。これは、ジェネリクスと配列の互換性を保つためです。しかし、instanceof演算子はジェネリクス型では使えないため、Class<T>のようなチェックが必要になることがあります。
ワイルドカードを慎重に使う
ワイルドカード(?)は、任意の型を受け入れる便利な機能ですが、使いすぎると型安全性が失われます。List<?> は読み取り専用、List<? extends Number> は書き込みも可能など、適切なPECSを使って、型パラメータを制限することをお勧めします。
ジェネリクス
// ジェネリッククラスpublic class Box<T> { private T value;
public void set(T value) { this.value = value; } public T get() { return value; }}
Box<String> stringBox = new Box<>();stringBox.set("Hello");String s = stringBox.get(); // キャスト不要
// ジェネリックメソッドpublic static <T> T first(List<T> list) { return list.isEmpty() ? null : list.get(0);}
String first = first(List.of("a", "b", "c")); // "a"
// 複数の型パラメータpublic class Pair<K, V> { private K key; private V value;}Hello\na
// ❌ Bad: Object でキャスト地獄List list = new ArrayList();list.add("text");String s = (String) list.get(0); // 危険// ✅ Good: ジェネリクスで型安全List<String> list = new ArrayList<>();list.add("text");String s = list.get(0); // キャスト不要ワイルドカード
// 境界付き型パラメータpublic <T extends Number> double sum(List<T> numbers) { return numbers.stream() .mapToDouble(Number::doubleValue) .sum();}
// ワイルドカードvoid printAll(List<?> list) { // 任意の型 for (Object item : list) { System.out.println(item); }}
// 上限境界(読み取り用)void process(List<? extends Number> nums) { Number n = nums.get(0); // OK // nums.add(1); // コンパイルエラー}
// 下限境界(書き込み用)void addIntegers(List<? super Integer> list) { list.add(1); // OK // Integer i = list.get(0); // コンパイルエラー}
// PECS: Producer Extends, Consumer Super