問題
ランタイム クラスを指定して型パラメーターを推論できるとは限りません。根本的な問題は、ジェネリック クラスのすべてのインスタンス (型パラメーターに関係なく) が同じランタイム クラスを共有することです。
new ArrayList<Integer>().getClass() == new ArrayList<String>().getClass()
したがって、そのクラスがジェネリックであり、そのクラスの異なるインスタンスが型パラメーターに異なる値を使用できる場合、ランタイム クラスだけが与えられた型パラメーターを検出することは不可能です。(はい、X<T extends String>
String が final であるため、 class の型パラメーターを見つけることができます。したがって、可能な唯一の引数です。同様に、ランタイム クラスが であることがわかっている場合、 List の型パラメーターを見つけることができますclass MappedList<T> implements List<String> { ... }
)。
対照的に、実行時クラスがジェネリックでない場合、スーパークラスの型パラメーターを推論することは常に可能です。これは、ネット上に浮かぶすべてのライブラリとコード例がさまざまな程度の洗練度で行っていることです。たとえば、次の場合:
class StringArrayList extends ArrayList<String> { ... }
ArrayList への型引数が であることを発見できますString
。これは、(ジェネリックでない限り) 匿名クラスでも機能します。
new ArrayList<String>() {}.getClass() != new ArrayList<Integer>() {}.getClass()
しかし、次のようなジェネリック クラスでは失敗します。
ArrayList<T> makeList(T t) {
return new ArrayList<T>(t) {};
}
なぜなら
makeList("2").getClass() == makeList(2).getClass()
ソリューション
実行時に型が必要な場合は、通常はClass
オブジェクトをコンストラクターに渡すなど、他の手段で型を提供する必要があります。また、スーパー タイプ トークン (リフレクションによって型パラメーターを検出できる、目的の型の非ジェネリックで通常は匿名のサブクラスのインスタンス) を使用することもできます。これには、それ自体がジェネリックな型パラメーターも表現できるという利点があります。
特定の目的のためだけにクラスが必要な場合は、より簡単な代替手段があるかもしれません。たとえば、型パラメーターの新しいインスタンスを作成する必要があるだけの場合は、ファクトリ (メソッド) パターンの方がかなり単純です :-)