在运行时获取generics类

我怎样才能做到这一点?

public class GenericClass<T> { public Type getMyType() { //How do I return the type of T? } } 

到目前为止我所尝试过的所有东西总是返回Objecttypes而不是使用的特定types。

正如其他人所说,只有在某些情况下才能通过反思。

如果您真的需要这种types,这是通常的(types安全的)解决方法模式:

 public class GenericClass<T> { private final Class<T> type; public GenericClass(Class<T> type) { this.type = type; } public Class<T> getMyType() { return this.type; } } 

我曾见过这样的事情

 private Class<T> persistentClass; public Constructor() { this.persistentClass = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; } 

在Hibernate GenericDataAccessObjects示例中

generics在运行时并不具体化 。 这意味着信息在运行时不存在。

向Java中添加generics,同时保持向后兼容性是一个参考(你可以看到有关它的开创性论文: 使未来安全:为Java编程语言增加通用性 )。

关于这个问题有很多文献,有的人对现状不满 ,有的说实际上是诱惑 ,没有实际的需要。 你可以阅读这两个链接,我发现他们很有趣。

你当然可以。

由于向后兼容的原因,Java在运行时不使用这些信息。 但是这些信息实际上是作为元数据存在的 ,可以通过reflection来访问(但是它仍然不能用于types检查)。

从官方的API:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

但是 ,对于你的情况,我不会使用reflection。 我个人更倾向于使用框架代码。 在你的情况下,我只是添加types作为构造函数参数。

使用番石榴。

 import com.google.common.reflect.TypeToken; import java.lang.reflect.Type; public class GenericClass<T> { private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { }; private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T> public Type getType() { return type; } public static void main(String[] args) { GenericClass<String> example = new GenericClass<String>() { }; System.out.println(example.getType()); // => class java.lang.String } } 

后来,我在这里发布了一些完整的例子,包括抽象类和子类。

注意:这要求您实例化GenericClass子类 ,以便它可以正确绑定types参数。 否则它只会返回typesT

Javagenerics大多是编译时间,这意味着types信息在运行时会丢失。

 class GenericCls<T> { T t; } 

将被编译成类似的东西

 class GenericCls { Object o; } 

要在运行时获取types信息,必须将其添加为ctor的参数。

 class GenericCls<T> { private Class<T> type; public GenericCls(Class<T> cls) { type= cls; } Class<T> getType(){return type;} } 

例:

 GenericCls<?> instance = new GenericCls<String>(String.class); assert instance.getType() == String.class; 

Ian Robertson在本文中描述的技术为我工作。

总之快速和肮脏的例子:

  public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V> { /** * Method returns class implementing EntityInterface which was used in class * extending AbstractDAO * * @return Class<T extends EntityInterface> */ public Class<T> returnedClass() { return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0); } /** * Get the underlying class for a type, or null if the type is a variable * type. * * @param type the type * @return the underlying class */ public static Class<?> getClass(Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class<?> componentClass = getClass(componentType); if (componentClass != null) { return Array.newInstance(componentClass, 0).getClass(); } else { return null; } } else { return null; } } /** * Get the actual type arguments a child class has used to extend a generic * base class. * * @param baseClass the base class * @param childClass the child class * @return a list of the raw classes for the actual type arguments. */ public static <T> List<Class<?>> getTypeArguments( Class<T> baseClass, Class<? extends T> childClass) { Map<Type, Type> resolvedTypes = new HashMap<Type, Type>(); Type type = childClass; // start walking up the inheritance hierarchy until we hit baseClass while (!getClass(type).equals(baseClass)) { if (type instanceof Class) { // there is no useful information for us in raw types, so just keep going. type = ((Class) type).getGenericSuperclass(); } else { ParameterizedType parameterizedType = (ParameterizedType) type; Class<?> rawType = (Class) parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); TypeVariable<?>[] typeParameters = rawType.getTypeParameters(); for (int i = 0; i < actualTypeArguments.length; i++) { resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); } if (!rawType.equals(baseClass)) { type = rawType.getGenericSuperclass(); } } } // finally, for each actual type argument provided to baseClass, determine (if possible) // the raw class for that type argument. Type[] actualTypeArguments; if (type instanceof Class) { actualTypeArguments = ((Class) type).getTypeParameters(); } else { actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); } List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>(); // resolve types by chasing down type variables. for (Type baseType : actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) { baseType = resolvedTypes.get(baseType); } typeArgumentsAsClasses.add(getClass(baseType)); } return typeArgumentsAsClasses; } } 

我不认为你可以,Java在编译时使用types擦除,所以你的代码与先前创build的应用程序和库兼容。

从Oracle文档:

types擦除

generics被引入到Java语言中以在编译时提供更严格的types检查,并支持generics编程。 为了实现generics,Java编译器将types擦除应用于:

如果types参数是无界的,则将genericstypes中的所有types参数replace为它们的边界或对象。 所产生的字节码因此只包含普通的类,接口和方法。 如果需要,插入types可以保持types安全。 生成桥接方法以保留扩展genericstypes中的多态性。 types擦除确保没有为参数化types创build新的类; 因此,generics不会导致运行时开销。

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

我用跟随的方法:

 public class A<T> { protected Class<T> clazz; public A() { this.clazz = (Class<>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } public Class<T> getClazz() { return clazz; } } public class B extends A<C> { /* ... */ public void anything() { // here I may use getClazz(); } } 

这是我的解决scheme:

 import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; public class GenericClass<T extends String> { public static void main(String[] args) { for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) { System.out.println(typeParam.getName()); for (Type bound : typeParam.getBounds()) { System.out.println(bound); } } } } 

这里是工作解决scheme!

 @SuppressWarnings("unchecked") private Class<T> getGenericTypeClass() {    try {        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();        Class<?> clazz = Class.forName(className);        return (Class<T>) clazz;    } catch (Exception e) {        throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");    } } 

注意: 只能用作超类
1.必须用types化类扩展( Child extends Generic<Integer>
要么
2.必须创build为匿名实现( new Generic<Integer>() {};

我认为还有另一个优雅的解决scheme。

你想要做的就是(安全地)将types参数的types从conceretetypes传递给超类。

如果您允许自己将类types视为类中的“元数据”,则表示在运行时对元数据进行编码的Java方法:注释。

首先沿这些行定义一个自定义注释:

 import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface EntityAnnotation { Class entityClass(); } 

然后你可以添加注释到你的子类。

 @EntityAnnotation(entityClass = PassedGenericType.class) public class Subclass<PassedGenericType> {...} 

然后你可以使用这个代码来获取基类中的类types:

 import org.springframework.core.annotation.AnnotationUtils; . . . private Class getGenericParameterType() { final Class aClass = this.getClass(); EntityAnnotation ne = AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class); return ne.entityClass(); } 

这种方法的一些限制是:

  1. 您可以在两个位置指定genericstypes( PassedGenericType ),而不是非DRY位置。
  2. 这是唯一可能的,如果你可以修改具体的子类。
 public abstract class AbstractDao<T> <br> { private final Class<T> persistentClass; public AbstractDao() { this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; } } 

你不能。 如果将Ttypes的成员variables添加到类中(甚至不需要初始化),则可以使用它来恢复types。

以防万一您使用通用types存储variables,您可以轻松地解决这个问题添加一个getClassType方法,如下所示:

 public class Constant<T> { private T value; @SuppressWarnings("unchecked") public Class<T> getClassType () { return ((Class<T>) value.getClass()); } } 

我稍后使用提供的类对象来检查它是否是给定类的实例,如下所示:

 Constant<?> constant = ...; if (constant.getClassType().equals(Integer.class)) { Constant<Integer> integerConstant = (Constant<Integer>)constant; Integer value = integerConstant.getValue(); // ... } 

这是我的窍门:

 public class Main { public static void main(String[] args) throws Exception { System.out.println(Main.<String> getClazz()); } static <T> Class getClazz(T... param) { return param.getClass().getComponentType(); } } 

为了完成这里的一些答案,我必须借助于recursion获得MyGenericClass的ParametrizedType,无论层次结构有多高:

 private Class<T> getGenericTypeClass() { return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0]; } private static ParameterizedType getParametrizedType(Class clazz){ if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy return (ParameterizedType) clazz.getGenericSuperclass(); } else { return getParametrizedType(clazz.getSuperclass()); } } 

这是我的解决scheme

 public class GenericClass<T> { private Class<T> realType; public GenericClass() { findTypeArguments(getClass()); } private void findTypeArguments(Type t) { if (t instanceof ParameterizedType) { Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments(); realType = (Class<T>) typeArgs[0]; } else { Class c = (Class) t; findTypeArguments(c.getGenericSuperclass()); } } public Type getMyType() { // How do I return the type of T? (your question) return realType; } } 

无论你的class级有多less层次,这个解决scheme仍然有效,例如:

 public class FirstLevelChild<T> extends GenericClass<T> { } public class SecondLevelChild extends FirstLevelChild<String> { } 

在这种情况下,getMyType()= java.lang.String

这里有一个方法,我不得不使用一两次:

 public abstract class GenericClass<T>{ public abstract Class<T> getMyType(); } 

随着

 public class SpecificClass extends GenericClass<String>{ @Override public Class<String> getMyType(){ return String.class; } } 

我发现这是一个简单易懂,易于解释的解决scheme

 public class GenericClass<T> { private Class classForT(T...t) { return t.getClass().getComponentType(); } public static void main(String[] args) { GenericClass<String> g = new GenericClass<String>(); System.out.println(g.classForT()); System.out.println(String.class); } }