コレクションのアイテムのタイプを取得する必要があります。単一インスタンスのクラスを取得するには、次のコードを使用します。
classUnderTest.getName()
しかし、どうすれば次のコレクションのアイテムのクラスを取得できますか?
Collection collection = (Collection) getCollection.invoke(instance1);
コレクションのアイテムのタイプを取得する必要があります。単一インスタンスのクラスを取得するには、次のコードを使用します。
classUnderTest.getName()
しかし、どうすれば次のコレクションのアイテムのクラスを取得できますか?
Collection collection = (Collection) getCollection.invoke(instance1);
一般に、それはできません。たとえば、コレクションが空であるか、null 値のみで構成されている場合があります。また、インターフェースはどうですか?すべてのコレクション要素に共通する複数のインターフェイスが存在する場合があります。
ただし、次のいずれかが役立つ場合があります。
1)コレクションの内容の分析: コレクションが空でないと仮定して、コレクション内のすべての項目をスキャンし、クラス階層内でそれらの最下位の共通の祖先を見つけます。
2)ジェネリック宣言型の取得: 検査しているオブジェクトが何らかのクラスのデータ メンバー (ジェネリックを含む) として宣言されている場合、リフレクションを使用して、実行時にその宣言されたジェネリック型を評価できます ( Field.getGenericType()を参照) 。
- 編集 -
練習のために、最も一般的な祖先ユーティリティを実装しました。
public static Class<?> getLCA(Collection<?> c) {
Class<?> res = null;
for (Object obj : c)
if (obj != null) {
Class<?> clazz = obj.getClass();
res = (res == null)? clazz : getLCA(res, clazz);
}
return res;
}
private static Class<?> getLCA(Class<?> classA, Class<?> classB) {
if (classA.isAssignableFrom(classB))
return classA;
if (classB.isAssignableFrom(classA))
return classB;
while (!classA.isAssignableFrom(classB))
classA = classA.getSuperclass();
return classA;
}
効率: O(n*h)。ここで、n はコレクション サイズ、h はクラス階層内のコレクション アイテムの最大深度です。
できません。Java では、実行時にジェネリック型を取得する方法はありません。これはType Erasureと呼ばれます。
しかし、 を繰り返し処理してCollection
、最も一般的なタイプを取得できます。しかし、そうしても、その型をジェネリック型として渡す方法はありません。
あなたの最善の策は、警告を抑制し、不正なものがメソッドに渡されないようにすることです。
答えは、ほとんどの人が言った投稿から明らかです...つまり、型消去のために、実行時にコレクションのジェネリック型を取得/推測できません
しかし、それが何を意味するのかを示したいと思います
仮定we create a class MyClass
してこれを行う
Collection<String> collection = new ArrayList<String>();
public static void main(String[] args) {
Field stringListField = null;
try {
stringListField = MyClass.class.getDeclaredField("collection");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
クラス java.lang.Stringを結果として出力します...つまり、 Collection オブジェクトが適切な汎用引数 Collection を使用していることを推測できることを意味します
しかし、私たちが置くならば、
Collection<?> collection = new ArrayList<String>();
その後、問題なくコンパイルされますが、実行すると次の例外が発生します..
sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to java.lang.Class
これは、ランタイム Type Erasure にスローされる例外です。
一般的な消去のため、Collection オブジェクトはアイテムがどのタイプである必要があるかを認識せず、現在のタイプのみを認識します。必要に応じて、コレクション内のすべてのアイテムの中で最も具体的なスーパークラス (またはスーパーインターフェイス) を見つけて、難しい方法で解決することが 1 つの解決策になる場合があります。
しかし、本当にコレクション アイテムの種類を知る必要があるのでしょうか。
Type Erasureが原因で、実行時に Collection のジェネリック型を取得/推測することはできません。ジェネリックはコンパイル時の安全のために存在し、型情報は実行時に消去されます。
実行中、インスタンスはObject
コレクション用であり、実行時の実際の型は気にしません。
短い答えは、できないということです。
ジェネリック型パラメーターは、型のインスタンス (オブジェクト) の一部として記録されません。(「タイプ消去」を読んでください...)さらに、あなたの例はとにかく生のタイプを使用しています。
(空ではない) コレクションの要素を選択してその型を取得できますが、他の要素の型については何もわかりません。(それらは同じかもしれません...または異なる場合があります。)そして、コレクションが空であるか、null
参照が含まれている場合、それを確実に行うことさえできません。
とにかく、これはコレクションの最初の要素の型を与えます。
Collection c = ...
Iterator it = c.iterator();
if (c.hasNext()) {
Class clazz = c.next().getClass();
...
}
コレクション全体を繰り返し処理し、(多少の困難はありますが) すべてが準拠している共通のクラスまたはインターフェイスを見つけ出すことができます。しかし、それでも宣言された要素の型と必ずしも同じではありません。