残念ながら、タイプの消去とリフレクション API の制限の組み合わせにより、あなたがしようとしていることは非常に複雑です。
Class.getGenericSuperclass
と の組み合わせを使用して、スーパークラスのジェネリック引数を取得できることは事実ですParameterizedType.getActualTypeArguments
。これは、たとえば Guava のTypeToken
クラスがジェネリック型引数をキャプチャするために使用するメカニズムです。ただし、ここで求めているのは、継承チェーンの任意の時点で実装されている可能性があるインターフェイスのジェネリック型引数です。ただし、新しい型パラメーターを自由に解決または宣言しながら、インターフェイス自体が相互に継承できます。
実証するには、次の方法を使用します。
static void inspect(Object o) {
Type type = o.getClass();
while (type != null) {
System.out.print(type + " implements");
Class<?> rawType =
(type instanceof ParameterizedType)
? (Class<?>)((ParameterizedType)type).getRawType()
: (Class<?>)type;
Type[] interfaceTypes = rawType.getGenericInterfaces();
if (interfaceTypes.length > 0) {
System.out.println(":");
for (Type interfaceType : interfaceTypes) {
if (interfaceType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)interfaceType;
System.out.print(" " + parameterizedType.getRawType() + " with type args: ");
Type[] actualTypeArgs = parameterizedType.getActualTypeArguments();
System.out.println(Arrays.toString(actualTypeArgs));
}
else {
System.out.println(" " + interfaceType);
}
}
}
else {
System.out.println(" nothing");
}
type = rawType.getGenericSuperclass();
}
}
これはオブジェクトに反映され、その継承チェーンを上って、実装されたインターフェイスとその一般的な引数 (該当する場合) について報告します。
リストした最初のケースで試してみましょう:
inspect(new ArrayList<SomeObject>());
これは以下を出力します:
class java.util.ArrayList implements:
interface java.util.List with type args: [E]
interface java.util.RandomAccess
interface java.lang.Cloneable
interface java.io.Serializable
java.util.AbstractList<E> implements:
interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing
E
型パラメーターが解決されていないことがわかります。これは、タイプの消去を考えると完全に理解できます。実行時に、に対応するバイトコード命令にnew ArrayList<SomeObject>()
は の概念がありませんSomeObject
。
匿名クラスの場合は異なります。
inspect(new Iterable<SomeObject>() {
@Override
public Iterator<SomeObject> iterator() {
throw new UnsupportedOperationException();
}
});
版画:
class sandbox.Main$1 implements:
interface java.lang.Iterable with type args: [class sandbox.SomeObject]
class java.lang.Object implements nothing
ここでは、匿名クラスが を実装して型パラメーターを解決したため、実行時に型引数を使用できますIterable<SomeObject>
。ListOfSomeObjects
そのサブクラスはどれも同じ理由で機能します。
E
では、継承チェーンの一部のクラスが途中で型パラメーターを解決する限り、それに対して照合できますか? 残念ながら、少なくとも上記の方法ではできません。
inspect(new ArrayList<SomeObject>() { });
これは以下を出力します:
class sandbox.Main$1 implements nothing
java.util.ArrayList<sandbox.SomeObject> implements:
interface java.util.List with type args: [E]
interface java.util.RandomAccess
interface java.lang.Cloneable
interface java.io.Serializable
java.util.AbstractList<E> implements:
interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing
の型引数は であることArrayList
がわかっていますがSomeObject
、そこで停止します。型パラメーター間に接続関係はありません。その理由は、次のコードです。
Class<?> rawType =
(type instanceof ParameterizedType)
? (Class<?>)((ParameterizedType)type).getRawType()
: (Class<?>)type;
Type[] interfaceTypes = rawType.getGenericInterfaces();
getGenericInterfaces
インターフェイスの型引数情報を取得する唯一の方法ですが、そのメソッドはClass
ではなくによって宣言されていType
ます。ParameterizedType
サブクラスのジェネリック性を表す状態を保持するインスタンスがメソッドにある場合は常に、強制的に を呼び出します。これは、型引数情報を持たないシングルトンgetRawType
を返します。Class
具体的な型引数で実装されたインターフェイスの型引数しか取得できないという結果になるのは、catch-22 です。
型引数とそれらが解決するパラメーターを一致させるリフレクション API メソッドを知りません。理論的には、継承チェーンを上って、実装されたクラスIterable
(またはサブインターフェース) を見つけ、対応する型引数に一致するまで下に戻るリフレクション コードを書くことができます。残念ながら、これがどのように実装されるかはわかりません。クラスは、任意の名前と任意の順序で型パラメーターを自由に宣言できるため、名前や位置に基づく単純なマッチングは不要です。たぶん、他の誰かが解決策に貢献できるでしょう。