简体   繁体   English

如何确定泛型的类?

[英]How to determine the class of a generic type?

I'm creating a generic class and in one of the methods I need to know the Class of the generic type currently in use. 我正在创建一个通用类,并且需要使用一种方法来了解当前使用的通用类型的Class。 The reason is that one of the method's I call expects this as an argument. 原因是我调用的方法之一期望将此作为参数。

Example: 例:

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

Clearly the example above doesn't work and results in the following error: Illegal class literal for the type parameter T. 显然,上面的示例不起作用,并导致以下错误:类型参数T的非法类文字。

My question is: does someone know a good alternative or workaround for this? 我的问题是:有人知道这个的好选择或解决方法吗?

Still the same problems : Generic informations are erased at runtime, it cannot be recovered. 仍然存在相同的问题:常规信息在运行时被删除,无法恢复。 A workaround is to pass the class T in parameter of a static method : 一种解决方法是在静态方法的参数中传递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();
    }
}

It's ugly, but it works. 很难看,但是可以用。

I was just pointed to this solution: 我只是被指出这种解决方案:

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

This works if A is given a concrete type by a subclass: 如果子类为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

Unfortunately Christoph's solution as written only works in very limited circumstances. 不幸的是,克里斯托夫撰写的解决方案仅在非常有限的情况下有效。 [EDIT: as commented below I no longer remember my reasoning for this sentence and it is likely wrong: "Note that this will only work in abstract classes, first of all."] The next difficulty is that g() only works from DIRECT subclasses of A . [编辑:如下所述,我不再记得我对这句话的推理了,这很可能是错误的:“请注意,首先,它仅在抽象类中起作用。”]下一个困难是g()仅对DIRECT有效A子类。 We can fix that, though: 不过,我们可以解决此问题:

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

This will work in many situations in practice, but not ALL the time. 实际上,这将在许多情况下起作用,但并非始终如此。 Consider: 考虑:

public class Foo<U,T extends Collection<?>> extends A<T> {}

(new Foo<String,List<Object>>() {}).g();

This will throw a ClassCastException , because the type argument here isn't a Class or a ParameterizedType at all; 这将引发ClassCastException ,因为这里的type参数根本不是ClassParameterizedType it's the TypeVariable T . 这是TypeVariable T So now you would be stuck trying to figure out what type T was supposed to stand for, and so on down the rabbit hole. 因此,现在您将不得不尝试找出T代表什么类型,以此类推。

I think the only reasonable, general answer is something akin to Nicolas's initial answer -- in general, if your class needs to instantiate objects of some other class that is unknown at compile-time, users of your class need to pass that class literal (or, perhaps, a Factory) to your class explicitly and not rely solely on generics. 我认为唯一合理的通用答案类似于Nicolas的初始答案-一般来说,如果您的类需要实例化在编译时未知的其他某个类的对象,则该类的用户需要传递该类文字(也许是工厂),而不是仅仅依赖泛型。

i find another way to obtain the Class of the generic object 我找到了获取通用对象的类的另一种方法

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

T can be resolved pretty easily using TypeTools : 使用TypeTools可以很容易地解决T:

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

I will elaborate on Christoph's solution. 我将详细介绍克里斯托夫的解决方案。

Here is the ClassGetter abstract class: 这是ClassGetter抽象类:

private abstract class ClassGetter<T> {
    public final Class<T> get() {
        final ParameterizedType superclass = (ParameterizedType)
            getClass().getGenericSuperclass();
        return (Class<T>)superclass.getActualTypeArguments()[0];
    }
}

Here is a static method which uses the above class to find a generic class' type: 这是一个静态方法,使用上面的类来查找泛型的类型:

public static <T> Class<T> getGenericClass() {
    return new ClassGetter<T>() {}.get();
}

As an example of it's usage, you could make this method: 作为其用法的示例,您可以使用以下方法:

public static final <T> T instantiate() {
    final Class<T> clazz = getGenericClass();
    try {
        return clazz.getConstructor((Class[])null).newInstance(null);
    } catch (Exception e) {
        return null;
    }
}

And then use it like this: 然后像这样使用它:

T var = instantiate();

public class DatabaseAccessUtil { 公共类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);    

}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM