Получить универсальный тип класса во время выполнения - PullRequest
442 голосов
/ 04 августа 2010

Как мне этого добиться?

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

Все, что я пробовал до сих пор, всегда возвращает тип Object, а не конкретный используемый тип.

Ответы [ 23 ]

286 голосов
/ 04 августа 2010

Как уже упоминалось, это возможно только путем отражения в определенных обстоятельствах.

Если вам действительно нужен тип, это обычный (безопасный для типов) способ обхода:

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}
228 голосов
/ 04 августа 2010

Я видел что-то подобное

private Class<T> persistentClass;

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

в спящих GenericDataAccessObjects Пример

87 голосов
/ 04 августа 2010

Обобщения не реифицированы во время выполнения.Это означает, что информация отсутствует во время выполнения.

Добавление обобщенных данных в Java при обеспечении обратной совместимости было непростой задачей (вы можете увидеть оригинальный документ об этом: Обеспечение безопасности в будущемдля прошлого: добавление универсальности к языку программирования Java ).

Существует обширная литература по этому вопросу, и некоторые люди недовольны текущим состоянием, некоторые говорят, чтона самом деле это приманка , и в этом нет особой необходимости.Вы можете прочитать обе ссылки, я нашел их довольно интересными.

58 голосов
/ 04 ноября 2013

Использование гуавы.

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
  }
}

Некоторое время назад я опубликовал несколько полноценных примеров, включая абстрактные классы и подклассы здесь .

Примечание: для этого требуется, чтобывы создаете экземпляр подкласса из GenericClass, чтобы он мог правильно связать параметр типа.В противном случае он просто вернет тип как T.

36 голосов
/ 04 августа 2010

Конечно, вы можете.

Java не использует информацию во время выполнения по причинам обратной совместимости.Но информация фактически представлена ​​ как метаданные и может быть доступна через отражение (но она все еще не используется для проверки типов).

Из официального API:

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

Однако , для вашего сценария я бы не использовал рефлексию.Я лично более склонен использовать это для кода платформы.В вашем случае я бы просто добавил тип в качестве конструктора param.

33 голосов
/ 04 августа 2010

Обобщения 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;
20 голосов
/ 28 сентября 2014

Я использовал следующий подход:

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();
    }
}
15 голосов
/ 21 февраля 2013

Техника, описанная в этой статье Иана Робертсона , работает для меня.

Короче, быстрый и грязный пример:

 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;
    }
  }
12 голосов
/ 15 февраля 2017
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
12 голосов
/ 04 августа 2010

Я не думаю, что вы можете, Java использует стирание типов при компиляции, чтобы ваш код был совместим с приложениями и библиотеками, которые были созданы предварительно.

Из документов Oracle:

Erasure типа

В язык Java были введены общие сведения для обеспечения более строгих проверок типов во время компиляции и для поддержки общего программирования.Для реализации обобщений Java-компилятор применяет стирание типов к:

Заменить все параметры типов в обобщенных типах их границами или Object, если параметры типа не ограничены.Таким образом, полученный байт-код содержит только обычные классы, интерфейсы и методы.Вставьте слепки типа при необходимости, чтобы сохранить безопасность типа.Генерация мостовых методов для сохранения полиморфизма в расширенных универсальных типах.Стирание типов гарантирует, что новые классы для параметризованных типов не создаются;следовательно, генерики не несут затрат времени выполнения.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...