15

実行時に宣言されたフィールドの汎用タイプにアクセスしようとしています。以前は、Java型消去のためにこれが不可能であるという印象を受けていました。ただし、よく知られているフレームワークの中には、実行時にリフレクションを介してジェネリック型を利用するものがあるため、これが当てはまらない場合があります。

例として、Guiceは指定したジェネリック型に基づいてプロバイダーを実装します。

public class Injectable{

    @Inject
    private Provider<SomeType> someTypeProvider;

}

リフレクションAPIを介して、フィールドの「SomeType」ジェネリック属性またはそのようなタイプ/メソッドなどにどのようにアクセスしますか?

さらに、Java 6 AnnotationProcessorAPIを介してこれらのジェネリック型属性にアクセスする方法も知っておくと役に立ちます。

ありがとう。

編集:

素晴らしいポインタをありがとうございました。ヘイレムのリンク、特にPrenkovの記事Java Reflection:Genericsへのリンクを使用してこれを行う方法を見つけました。

これが私が探していた答えです:

/**
 * @author John Ericksen
 */
public class TypeReflectionExample {

    public class SomeType{}

    public class Injectable{
        @Inject  private Provider<SomeType> someTypeProvider;
    }

    public static void main(String[] args){

        try {
            Field providerField = Injectable.class.getDeclaredField("someTypeProvider");

            Type genericFieldType = providerField.getGenericType();

            if(genericFieldType instanceof ParameterizedType){
                ParameterizedType aType = (ParameterizedType) genericFieldType;
                Type[] fieldArgTypes = aType.getActualTypeArguments();
                for(Type fieldArgType : fieldArgTypes){
                    Class fieldArgClass = (Class) fieldArgType;
                    System.out.println("fieldArgClass = " + fieldArgClass);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }   
}

結果:

fieldArgClass=クラスtest.TypeReflectionExample$SomeType

メソッド、コンストラクター、スーパークラスの拡張機能/実装などについても同じことができます。

彼の投稿が私の質問に直接答えなかったとしても、私をこの解決策に導いたので、私はヘイレムを授与しています。

4

4 に答える 4

15

ジェネリックは型消去で実装されているため、Javaでは実行時に一般的に知られていないのは事実です。

ジェネリックを反映していますか?

ただし、IanRoberstonの記事ReflectingGenericsおよびPrenkovの記事JavaReflection :Genericsに示されているように、宣言された型(ランタイムオブジェクトの型ではない)に関するいくつかの貴重な情報を抽出することができます。

ジェネリックスと型消去の背景

ジェネリックは、ソースqndバイナリレベルで下位互換性を維持しながら導入されたため、次のような制限があります。

  • ジェネリックスサポート(ここでは、いわゆるダイアモンド演算子 <>)の少なくともいくつかのインジケーターなしで速記形式を持つことは不可能です、
  • ジェネリック型は型消去で実装する必要があったため、実行時に検査することはできません。

参考文献

于 2012-03-03T18:46:54.113 に答える
4

さて、guiceが何をしているのか見てみませんか?ソースは公開されています。

Guiceはこれらのことを複数のレベルで行います。特に目立つのは、型リテラルです。

ここで重要なのは、型は型消去を使用してコンパイルされますが(したがって、型ごとに1つのクラスしかない)、Type使用されるジェネリックを知っている複数のオブジェクトがまだ存在するということです。ただし、コードはそれとは関係なく最適化されますタイプごとではなく、クラスごとにコンパイルされたため)。

のJavaAPIをご覧くださいParameterizedType

したがって、Java Genericsがクラスレベルで「型消去」によって実装されることは正しいですが、これはレベルで完全に保持されるわけではありませんType。残念ながら、Typesの処理はクラスよりもはるかに注意が必要です。さらに、これは、特にプリミティブ型の場合、JavaがC++と同じ方法でジェネリックスを最適化できないことも意味します。ArrayList<Integer>意志は設計上、オブジェクトArrayList<?>を含み、int[]可能な場合はネイティブ配列に支えられていません。

ただし、これはこれらのことを自分で追跡することにかなり近いことに注意してください。たとえば、非常に単純に(ネストされたジェネリックでは機能しません)、ArrayList<T>フィールドを持つクラスで拡張するだけClass<T> contentClassで、実行時にこの情報を見つけることができます。(ただし、ここではクラスの代わりにTypeLiteralの方が適している場合があります!)さらに、JREは、リストの一貫性を維持することを実際には保証しません。ArrayList<Integer>型なしにをキャストしてオブジェクトArrayListを追加できるのと同じように。String

于 2012-03-03T18:58:14.327 に答える
2

私はこれを1年ほど前に使用しました。それはあなたを助けることができます

Type typeOfSrc = type(YourClass.class, clazz);

// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
    return new ParameterizedType()
    {
        public Type getRawType(){ return raw; }

        public Type[] getActualTypeArguments(){ return args; }

        public Type getOwnerType(){ return null; }
    };
}

これにより、実行時にジェネリック型にアクセスできます。基本的に、ここで行っているのは、汎用情報を別のクラスに格納し、そのクラスを使用して実行時にその情報を取得することです。

于 2012-03-03T19:13:33.613 に答える
0

guiceはTypeLiteralを使用して、一般的な情報を別のオブジェクトにカプセル化すると思います。

于 2012-03-03T20:22:25.683 に答える