Как определить класс универсального типа?


Я создаю универсальный класс и в одном из методов мне нужно знать класс универсального типа, который в настоящее время используется. Причина в том, что один из методов, который я вызываю, ожидает этого в качестве аргумента.

пример:

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

очевидно, что приведенный выше пример не работает и приводит к следующей ошибке: недопустимый литерал класса для параметра типа T.

мой вопрос: кто-нибудь знает хорошую альтернативу или обходной путь для этого?

7 57

7 ответов:

все те же проблемы : общие сведения стираются во время выполнения, они не могут быть восстановлены. Обходной путь-передать класс 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();
    }
}

Это некрасиво, но это работает.

мне просто указали на это решение:

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

к сожалению, решение Кристофа, как написано, работает только в очень ограниченных обстоятельствах. [EDIT: как указано ниже, я больше не помню свои рассуждения для этого предложения, и это, вероятно, неправильно: "обратите внимание, что это будет работать только в абстрактных классах, прежде всего."] Следующая трудность заключается в том, что g() работает только из прямых подклассов A. Мы можем это исправить, хотя:

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 должен был стоять, и так далее вниз по кроличьей норе.

Я думаю, что единственный разумный, общий ответ-это что-то похожее на первоначальный ответ Николаса - в общем, если ваш класс должен создавать экземпляры объектов какого-то другого класса, который неизвестен в во время компиляции пользователи вашего класса должны передать этот литерал класса (или, возможно, фабрику) в ваш класс явно и не полагаться исключительно на универсальные шаблоны.

Я нахожу другой способ получить класс универсального объекта

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

я подробно остановлюсь на решении Кристофа.

вот абстрактный класс 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();

T может быть решена довольно легко с помощью TypeTools:

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

public class DatabaseAccessUtil {

EntityManagerFactory entitymanagerfactory;
EntityManager entitymanager;

public DatabaseAccessUtil() {
    entitymanagerfactory=Persistence.createEntityManagerFactory("bookmyshow");
    entitymanager=entitymanagerfactory.createEntityManager();
}

public void save  (T t) {
    entitymanager.getTransaction().begin();
    entitymanager.persist(t);
    entitymanager.getTransaction().commit();
}

public void update(T t) {
    entitymanager.getTransaction().begin();
    entitymanager.persist(t);
    entitymanager.getTransaction().commit();
}

public void delete(T t) {
    entitymanager.getTransaction().begin();
    entitymanager.remove(t);
    entitymanager.getTransaction().commit();
}

public Object retrieve(Query query) {
    return query.getSingleResult();
}
//call the method - retrieve(object,requiredclass.class)
public Object retrieve(Object primaryKey,class clazz) throws Exception {

    return entitymanager.find(clazz,primaryKey);    

}

}