5.30:ジェネリックメソッドを使う
要点
クラスの型パラメータに頼らず、メソッド単位で型パラメータを宣言して汎用処理を安全に書く — それがジェネリックメソッド。
(特にユーティリティ/ファクトリ/変換系で有用)
なぜ使うか(メリット)
- 呼び出し側の型推論でキャスト不要・安全に使える。
- 同じ実装で複数の型を扱える(再利用性)。
- クラスがジェネリックでなくても(あるいは static メソッドでも)汎用的処理が書ける。
- 型の上限/下限を指定してコンパイル時に誤使用を防げる。
基本構文
// 型パラメータ宣言: public static <T> 戻り値 型名(引数...)
public static <T> T getFirst(List<T> list) {
return list.get(0);
}
- 最初の < T > が メソッド型パラメータの宣言。
- その後の T は戻り値や引数で使われる型。
よく使うパターン
1. 単純な汎用メソッド
public static <T> T getFirst(List<T> list) {
if (list.isEmpty()) throw new NoSuchElementException();
return list.get(0);
}
2) 境界付き型パラメータ(上限)
// T は Comparable のサブタイプでなければならない
public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll) {
Iterator<? extends T> it = coll.iterator();
T candidate = it.next();
while (it.hasNext()) {
T next = it.next();
if (next.compareTo(candidate) > 0) candidate = next;
}
return candidate;
}
(< ? super T > を使うのは「汎用的な Comparable の扱い」を安全にするため)
3. 複数型パラメータ(ファクトリ)
public static <K,V> Map<K,V> singletonMap(K k, V v) {
Map<K,V> m = new HashMap<>();
m.put(k, v);
return Collections.unmodifiableMap(m);
}
4. varargs と組み合わせ(@SafeVarargs の注意付き)
@SafeVarargs
public static <T> List<T> listOf(T... elems) {
List<T> list = new ArrayList<>(elems.length);
Collections.addAll(list, elems); // elems をコピーして配列を露出しない
return list;
}
→ Arrays.asList(elems) のように配列をバックにする実装は危険(配列露出・heap pollution)なので避ける。
注意点
-
型消去の制約:
new T[] や instanceof List< String > は使えない(実行時に型情報が消える)。
必要なら Class< T > を引数に取って Array.newInstance(...) を使う。 - ジェネリック配列は避ける。 List< T >[] の生成は危険。
- varargs + ジェネリクス は heap pollution を招く可能性があるため、@SafeVarargs を使う場合は配列を露出しない実装にする/static または final メソッドに限定する。
-
無検査キャストを出さない:
可能なら @SuppressWarnings("unchecked") は使わず、どうしてもなら局所化して理由をコメントする。