web-dev-qa-db-ja.com

実行時にクラスのジェネリック型を取得する

どうすればこれを達成できますか?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

これまでに試したことはすべて、使用している特定の型ではなく、常にObject型を返すことです。

432
Glenn

他の人が述べたように、それは特定の状況での反射によってのみ可能です。

本当に型が必要な場合は、これが通常の(型保証された)回避策のパターンです。

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}
280
Henning

私はこのようなものを見ました

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

休止状態のGenericDataAccessObjects の例

225
FrVaBe

ジェネリックは、実行時にはreifiedではありません。これは、実行時に情報が存在しないことを意味します。

後方互換性を維持しながらジェネリックをJavaに追加することは、必然的でした(それに関する独創的な論文を見ることができます: 過去の未来を安全にする:Javaプログラミング言語 )。

主題に関する豊富な文献があり、一部の人々は現在の状態に 不満を抱いています 、実際にはそれが ルアー であると言う人もいます本当に必要はありません。あなたは両方のリンクを読むことができます、私はそれらが非常に面白いとわかりました。

88
ewernli

グアバを使用してください。

import com.google.common.reflect.TypeToken;
import Java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class Java.lang.String
  }
}

しばらく前に、抽象クラスとサブクラス ここ を含む本格的な例をいくつか投稿しました。

注:これには、typeパラメーターを正しくバインドできるように、GenericClass サブクラス をインスタンス化する必要があります。そうでなければ、単に型をTとして返します。

54
Cody A. Ray

もちろんできます。

下位互換性の理由から、Javaは実行時に情報をuseしません。しかし、その情報はメタデータとして実際に存在するであり、リフレクションを介してアクセスすることができます(ただし、型チェックにはまだ使用されません)。

公式のAPIから:

http://download.Oracle.com/javase/6/docs/api/Java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

しかし 、あなたのシナリオではリフレクションは使いません。私は個人的にそれをフレームワークコードに使う傾向があります。あなたの場合は、コンストラクタパラメータとして型を追加するだけです。

36
Joeri Hendrickx

Javaジェネリックはほとんどコンパイル時です。これは型情報が実行時に失われることを意味します。

class GenericCls<T>
{
    T t;
}

のようにコンパイルされます

class GenericCls
{
   Object o;
}

実行時に型情報を取得するには、それをctorの引数として追加する必要があります。

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

例:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
33
josefx

私はフォローアプローチを使用しました:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}
21
Moesio

これに記述されたテクニック Ian Robertsonによる記事 は私のために働きます。

手短に汚い例では:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }
14
Ondrej Bozek

Javaはコンパイル時に型消去を使用するので、コードは事前総称で作成されたアプリケーションやライブラリと互換性があります。

Oracle Docsから:

消去タイプ

Java言語にジェネリックが導入され、コンパイル時の型チェックが強化され、ジェネリックプログラミングがサポートされました。総称を実装するために、Javaコンパイラは型消去を次のものに適用します。

型パラメータが無制限の場合は、総称型のすべての型パラメータをそれらの境界またはObjectに置き換えます。したがって、生成されたバイトコードには、通常のクラス、インタフェース、およびメソッドだけが含まれています。型安全を保つために必要ならば型キャストを挿入する。拡張ジェネリック型で多型を維持するためのブリッジメソッドを生成します。型を消去すると、パラメータ化された型に対して新しいクラスが作成されなくなります。その結果、ジェネリックはランタイムオーバーヘッドを被りません。

http://docs.Oracle.com/javase/tutorial/Java/generics/erasure.html

12
Dimitar
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
10
Ali Yeganeh

これが私の解決策です:

import Java.lang.reflect.Type;
import Java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}
8
Matthias M

私はもう一つの優雅な解決策があると思います。

あなたがしたいことは、(安全に)ジェネリック型パラメータの型を、コンセントクラスからスーパークラスに渡すことです。

クラス型をクラスの「メタデータ」と見なすことができるのであれば、実行時にメタデータをエンコードするためのJavaメソッド、つまり注釈を推奨します。

まずこれらの行に沿ってカスタムアノテーションを定義します。

import Java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

その後、アノテーションをサブクラスに追加する必要があります。

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

その後、このコードを使用して基本クラスのクラスタイプを取得できます。

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

このアプローチのいくつかの制限は以下のとおりです。

  1. ジェネリック型(PassedGenericType)は、非DRY型ではなく2つの場所で指定します。
  2. これは、具象サブクラスを変更できる場合にのみ可能です。
8
DaBlick

これが解決策です!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

注: スーパークラスとしてのみ使用できます
1。型付きクラス(Child extends Generic<Integer>)で拡張する必要があります
OR

2。匿名実装として作成する必要があります(new Generic<Integer>() {};

5
Yaroslav Kovbas

このタクシーの1つの簡単なソリューションは次のようになります

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}
3
Paul92

これは私が一度か二度使用しなければならなかった1つの方法です:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

に加えて

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
3
PJWeisberg

できません。 T型のメンバ変数をクラスに追加した場合(初期化する必要さえない)、それを使用して型を回復することができます。

3
andrewmu

ここでいくつかの答えを完成させるために、再帰の助けを借りて、階層の高さに関係なく、MyGenericClassのParametrizedTypeを取得する必要がありました。

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}
2
galex

念のため、ジェネリック型を使用して変数を保存する場合は、次のようにgetClassTypeメソッドを追加することでこの問題を簡単に解決できます。

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

次のように、提供されたクラスオブジェクトを使用して、それが特定のクラスのインスタンスかどうかを確認します。

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
1
Pablo Trinidad

これが私の解決策です

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

クラス階層のレベルがいくつであっても、この解決策は有効です。次に例を示します。

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

この場合、getMyType()= Java.lang.String

1
Lin Yu Cheng

これが私のトリックです:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}
1
HRgiger
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
0
kayz1