Как определить класс универсального типа?
Я создаю универсальный класс и в одном из методов мне нужно знать класс универсального типа, который в настоящее время используется. Причина в том, что один из методов, который я вызываю, ожидает этого в качестве аргумента.
пример:
public class MyGenericClass<T> {
public void doSomething() {
// Snip...
// Call to a 3rd party lib
T bean = (T)someObject.create(T.class);
// Snip...
}
}
очевидно, что приведенный выше пример не работает и приводит к следующей ошибке: недопустимый литерал класса для параметра типа T.
мой вопрос: кто-нибудь знает хорошую альтернативу или обходной путь для этого?
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
вообще, этоTypeVariable
T
. Так что теперь вы застряли бы, пытаясь выяснить, какой тип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); }
}