我正在创建一个泛型类,在其中一个方法中,我需要知道当前使用的泛型类型的class。原因是其中一个方法的I调用期望将其作为参数。
示例:
public class MyGenericClass<T> {
public void doSomething() {
// Snip...
// Call to a 3rd party lib
T bean = (T)someObject.create(T.class);
// Snip...
}
}
显然,上面的例子不起作用,并导致以下错误:类型参数t的非法类文字。
我的问题是:有人知道一个好的替代方案或变通方法吗?
仍然存在同样的问题:通用信息在运行时被擦除,无法恢复。一种变通方法是在静态方法的参数中传递类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
不幸的是,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
;它是TypeVariable
T
。所以现在你会被困在试图弄清楚T
应该代表什么类型,等等。
我认为唯一合理、通用的答案类似于Nicolas的初始答案——一般来说,如果你的类需要实例化编译时未知的其他类的对象,那么你的类的用户需要将该类文本(或者,可能是Factory)显式传递给你的类,而不仅仅依赖泛型。
我找到另一种方法来获得通用对象的class
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;
}
我将详细介绍Christoph的解决方案。
这是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();
公共类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);
}
}