web-dev-qa-db-ja.com

ジェネリック型のクラスを決定する方法は?

ジェネリッククラスを作成していますが、メソッドの1つで、現在使用されているジェネリック型のクラスを知る必要があります。その理由は、私が呼び出すメソッドの1つがこれを引数として期待しているからです。

例:

public class MyGenericClass<T> {
  public void doSomething() {
    // Snip...
    // Call to a 3rd party lib
    T bean = (T)someObject.create(T.class);
    // Snip...
  }
}

明らかに上記の例は機能せず、次のエラーが発生します。型パラメーターTのクラスリテラルが無効です。

私の質問は次のとおりです。誰かがこれの良い代替案または回避策を知っていますか?

59
Jaap Coomans

まだ同じ問題:一般情報は実行時に消去され、回復できません。回避策は、静的メソッドのパラメーターでクラスTを渡すことです。

public class MyGenericClass<T> {

    private final Class<T> clazz;

    public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
        return new MyGenericClass<U>(clazz);
    }

    protected MyGenericClass(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void doSomething() {
        T instance = clazz.newInstance();
    }
}

Ugいですが、動作します。

47
Nicolas

私はこの解決策を指摘されました:

import Java.lang.reflect.ParameterizedType;

public abstract class A<B> {
    public Class<B> g() throws Exception {
        ParameterizedType superclass =
            (ParameterizedType) getClass().getGenericSuperclass();

        return (Class<B>) superclass.getActualTypeArguments()[0];
    }
}

これは、Aがサブクラスによって具象型を与えられた場合に機能します:

new A<String>() {}.g() // this will work

class B extends A<String> {}
new B().g() // this will work

class C<T> extends A<T> {}
new C<String>().g() // this will NOT work
22
Christoph

残念ながら、書かれたクリストフのソリューションは、非常に限られた状況でのみ機能します。 [編集:以下にコメントするように、私はこの文の理由をもはや覚えておらず、間違っている可能性が高い: "これはまず抽象クラスでのみ機能することに注意してください。"]次の難しさはg()AのDIRECTサブクラスからのみ機能します。ただし、それは修正できます。

private Class<?> extractClassFromType(Type t) throws ClassCastException {
    if (t instanceof Class<?>) {
        return (Class<?>)t;
    }
    return (Class<?>)((ParameterizedType)t).getRawType();
}

public Class<B> g() throws ClassCastException {
    Class<?> superClass = getClass(); // initial value
    Type superType;
    do {
        superType = superClass.getGenericSuperclass();
        superClass = extractClassFromType(superType);
    } while (! (superClass.equals(A.class)));

    Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0];
    return (Class<B>)extractClassFromType(actualArg);
}

これは実際には多くの状況で機能しますが、常に機能するわけではありません。考慮してください:

public class Foo<U,T extends Collection<?>> extends A<T> {}

(new Foo<String,List<Object>>() {}).g();

これはClassCastExceptionをスローします。これは、型引数がClassまたはParameterizedTypeではないためです。 TypeVariableTです。だから今、あなたはどのタイプTが代わるべきであるかを把握しようとして立ち往生しているでしょう。

唯一の合理的で一般的な答えは、ニコラスの最初の答えに似ていると思います-一般に、クラスがコンパイル時に不明な他のクラスのオブジェクトをインスタンス化する必要がある場合、クラスのユーザーはそのクラスリテラルを渡す必要があります(または、おそらくファクトリ)を明示的にクラスに追加し、ジェネリックだけに依存しないようにします。

4
Steven Collins

ジェネリックオブジェクトのクラスを取得する別の方法を見つける

public Class<?> getGenericClass(){
         Class<?> result =null;
         Type type =this.getClass().getGenericSuperclass();

         if(type instanceofParameterizedType){
              ParameterizedType pt =(ParameterizedType) type;
              Type[] fieldArgTypes = pt.getActualTypeArguments();
              result =(Class<?>) fieldArgTypes[0];
        }
        return result;
  }
2
Jet Geng

Tは TypeTools を使用して簡単に解決できます。

Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
                                MyGenericClass.class, getClass());
0
Jonathan

クリストフのソリューションについて詳しく説明します。

ClassGetter抽象クラスは次のとおりです。

private abstract class ClassGetter<T> {
    public final Class<T> get() {
        final ParameterizedType superclass = (ParameterizedType)
            getClass().getGenericSuperclass();
        return (Class<T>)superclass.getActualTypeArguments()[0];
    }
}

上記のクラスを使用してジェネリッククラスの型を見つける静的メソッドを次に示します。

public static <T> Class<T> getGenericClass() {
    return new ClassGetter<T>() {}.get();
}

その使用例として、次のメソッドを作成できます。

public static final <T> T instantiate() {
    final Class<T> clazz = getGenericClass();
    try {
        return clazz.getConstructor((Class[])null).newInstance(null);
    } catch (Exception e) {
        return null;
    }
}

そして、次のように使用します:

T var = instantiate();
0
intrepidis