ジェネリクスは、型パラメータを持つクラス、インターフェース、メソッドを作成することを可能にし、コンパイル時に型のチェックを行い、ClassCastExceptionを回避するのに役立ちます。
主な特徴と注意点:
new List<String>[10] — コンパイルエラー。T obj = new T();if(obj instanceof List<String>) — エラー。? extends T — 単調増加(読み取り)、? super T — 単調減少(書き込み)。例:
// 読み取りのための単調増加アプローチ void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // 書き込みのための単調減少アプローチ void addIntegers(List<? super Integer> list) { list.add(10); }
質問: 「List<Object>とList<?>の違いは何ですか?List<?>に任意のオブジェクトを追加できますか?」
回答: いいえ、List<?>には何も追加できません(nullを除く)、コンパイラはそこにどの型パラメータがあるかを知らないからです。一方、List<Object>には任意のオブジェクトを追加できます。
例:
List<?> list1 = new ArrayList<String>(); // list1.add("test"); // コンパイルエラー! List<Object> list2 = new ArrayList<>(); list2.add("test"); // OK
ストーリー
開発チームは、パラメータ化された型
T[]に基づくキャッシュを実装しようとしました。型消去とジェネリック型の配列を作成できないことから、解決策は期待通りに機能しませんでした:得られたのはObject[]であり、runtimeキャスト時にClassCastExceptionを引き起こしました。
ストーリー
あるマイクロサービスで、開発者はList<?>をパラメータとして使用するレシーバーを実装しようとし、コレクションの変更を試みました。これによりコンパイルエラーが発生し、PECSに従ってロジックをリファクタリングする必要があり、リリースの遅延を引き起こしました。
ストーリー
外部システムとの統合プロジェクトで、開発者は未処理のローフィールドを通じて異なる型のコレクションを上書きするというエラーを犯し、List list = new ArrayList<String>()としたため、ClassCastExceptionが発生し、本番環境で他の型にキャストしようとした際にサービスがクラッシュしました。