5

特定のターゲット タイプ (実行時に決定) と、それと比較する反復可能なクラスがあります。クラスのジェネリック パラメーターをチェックするメソッドを作成して、対象の型をサブクラス化する何かのイテラブルかどうかを確認しようとしています。例:

Class<?> X = SomeObject.class;

matches(X, new ArrayList<SomeObject>()) -> true
matches(X, new ArrayList<SubclassOfSomeObject>()) -> true
matches(X, new ArrayList<SomeOtherObject>()) -> false
matches(X, new ArrayList()) -> true (I think?)
matches(X, new Iterable<SomeObject>() { ... }) -> true
matches(X, new ListOfSomeObjects()) -> true 
                       (where ListOfSomeObjects extends Iterable<SomeObject>)
4

2 に答える 2

12

残念ながら、タイプの消去とリフレクション 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(またはサブインターフェース) を見つけ、対応する型引数に一致するまで下に戻るリフレクション コードを書くことができます。残念ながら、これがどのように実装されるかはわかりません。クラスは、任意の名前と任意の順序で型パラメーターを自由に宣言できるため、名前や位置に基づく単純なマッチングは不要です。たぶん、他の誰かが解決策に貢献できるでしょう。

于 2013-02-02T08:04:19.903 に答える
1

単なるインスタンスからジェネリック パラメータ タイプを取得できるとは思いません。

new ArrayList<Integer>();

インスタンスを保持するフィールドがある場合は、そこから取得できます。

private List<Integer> list = ArrayList<Integer>();

この同様の質問を参照してください: get generic type of java.util.List

于 2013-02-02T01:59:36.320 に答える