在运行时获取generics类
我怎样才能做到这一点?
public class GenericClass<T> { public Type getMyType() { //How do I return the type of T? } }
到目前为止我所尝试过的所有东西总是返回Object
types而不是使用的特定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:
但是 ,对于你的情况,我不会使用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(); }
这种方法的一些限制是:
- 您可以在两个位置指定genericstypes(
PassedGenericType
),而不是非DRY位置。 - 这是唯一可能的,如果你可以修改具体的子类。
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); } }