Java 如何确定泛型类型的类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/182636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to determine the class of a generic type?
提问by Jaap Coomans
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. 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?
我的问题是:有人知道一个好的替代方法或解决方法吗?
采纳答案by Nicolas
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.
这很丑陋,但它有效。
回答by Christoph
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
回答by Steven Collins
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
. We can fix that, though:
不幸的是,克里斯托夫的书面解决方案仅适用于非常有限的情况。[编辑:正如下面所评论的,我不再记得我对这句话的推理,它可能是错误的:“请注意,这首先只适用于抽象类。”] 下一个困难是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);
}
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; it's the 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.
这将抛出 a ClassCastException
,因为这里的类型参数根本不是 aClass
或 a ParameterizedType
;这是TypeVariable
T
. 所以现在你会被困在试图弄清楚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 的初始答案——一般来说,如果您的类需要实例化某个在编译时未知的其他类的对象,则您的类的用户需要传递该类文字(或者,也许,一个工厂)显式地添加到您的类中,而不仅仅依赖于泛型。
回答by Jet Geng
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;
}
回答by Jonathan
回答by intrepidis
I will elaborate on Christoph's solution.
我将详细说明 Christoph 的解决方案。
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();
回答by Abul Kasim M
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);
}
}
}