在运行时获取类的泛型类型

我怎样才能做到这一点呢?

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

到目前为止,我所尝试的一切总是返回类型Object,而不是使用的特定类型。

779584 次浏览

我不认为你可以,Java在编译时使用类型擦除,这样你的代码就可以与在泛型之前创建的应用程序和库兼容。

来自Oracle文档:

类型擦除

Java语言引入泛型是为了提供更紧凑的类型 在编译时检查并支持泛型编程。来 实现泛型,Java编译器应用类型擦除:

将泛型类型中的所有类型参数替换为它们的边界或 如果类型参数是无界的,则为。产生的字节码, 因此,只包含普通的类、接口和方法。 必要时插入类型强制转换以保持类型安全。生成 桥接方法以保存扩展泛型类型中的多态性。 类型擦除确保不为参数化创建新的类 类型;因此,泛型不会产生运行时开销

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

你不能。如果向类中添加类型为T的成员变量(甚至不需要初始化它),则可以使用它来恢复类型。

泛型在运行时不是具体化。这意味着信息在运行时不存在。

在向Java中添加泛型的同时保持向后兼容性是一项极好的工作(您可以看到关于它的开创性论文:让未来比过去更安全:向Java编程语言添加泛型)。

关于这个主题有丰富的文献,有些人用不满意表示当前状态,有些人说实际上它是吸引,没有真正的需要。你可以阅读这两个链接,我发现它们很有趣。

我见过类似的东西

private Class<T> persistentClass;


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

hibernate GenericDataAccessObjects示例中

正如其他人提到的,只有在特定的情况下通过反思才有可能。

如果你真的需要这个类型,这是常见的(类型安全的)变通模式:

public class GenericClass<T> {


private final Class<T> type;


public GenericClass(Class<T> type) {
this.type = type;
}


public Class<T> getMyType() {
return this.type;
}
}

Java泛型大多是在编译时,这意味着类型信息在运行时丢失。

class GenericCls<T>
{
T t;
}

会被编译成什么样子

class GenericCls
{
Object o;
}

要在运行时获得类型信息,必须将其作为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;

当然,你可以。

出于向后兼容的原因,Java在运行时不使用使用信息。但是信息是作为元数据的实际上现在,并且可以通过反射访问(但是它仍然不用于类型检查)。

来自官方API:

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

然而,对于你的场景,我不会使用反射。我个人更倾向于将其用于框架代码。在你的例子中,我只是将类型作为构造函数参数添加。

这是我的解决方案:

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

文章作者: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;
}
}

这里有一个方法,我用过一两次:

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

随着

public class SpecificClass extends GenericClass<String>{


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

使用番石榴。

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;


public abstract 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子类,这样它才能正确绑定类型参数。否则它将返回类型为T

以下是我的诀窍:

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


}

如果你使用泛型类型存储变量,你可以很容易地解决这个问题,添加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 A<T> {


protected Class<T> clazz;


public A() {
this.clazz = (Class<T>) ((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();
}
}

这里是工作解决方案!!

@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 <> ");
    }
}
< p >指出: 只能作为超类使用
1。必须扩展类型化类(Child extends Generic<Integer>)

2。必须创建为匿名实现(new Generic<Integer>() {};)

为了完成这里的一些答案,我必须得到MyGenericClass的paramtrizedtype,不管层次结构有多高,在递归的帮助下:

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

我认为还有另一种优雅的解决方案。

你要做的是(安全地)将泛型类型参数的类型从concerete类“传递”给父类。

如果您允许自己将类类型视为类上的“元数据”,则建议使用Java方法在运行时编码元数据:注释。

首先按照下面的代码定义一个自定义注释:

import java.lang.annotation.*;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}

然后必须将注释添加到子类。

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

然后你可以使用下面的代码来获取基类中的类类型:

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. 在两个地方指定泛型类型(PassedGenericType),而不是一个非dry类型。
  2. 只有当您可以修改具体的子类时,这才有可能。

这是我的解决方案

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;
}
}
不管你的类层次结构有多少层, 这个解决方案仍然有效,例如:

public class FirstLevelChild<T> extends GenericClass<T> {


}


public class SecondLevelChild extends FirstLevelChild<String> {


}

在本例中,getMyType() = java.lang.String

我发现这是一个简单易懂且容易解释的解决方案

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);
}
}
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;


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

一个简单的解决方案,这种出租车如下

public class GenericDemo<T>{
private T type;


GenericDemo(T t)
{
this.type = t;
}


public String getType()
{
return this.type.getClass().getName();
}


public static void main(String[] args)
{
GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
System.out.println("Type: "+ obj.getType());
}
}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
它可能对某人有用。你可以使用java.lang.ref.WeakReference; : < / p >
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;


N getType(){
return variableToGetTypeFrom.get();
}
}

我做了相同的@Moesio上面,但在Kotlin可以这样做:

class A<T : SomeClass>() {


var someClassType : T


init(){
this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
}


}

这是我的解决方案。例子可以解释。唯一的要求是子类必须设置泛型类型,而不是对象。

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;


public class TypeUtils {


/*** EXAMPLES ***/


public static class Class1<A, B, C> {


public A someA;
public B someB;
public C someC;


public Class<?> getAType() {
return getTypeParameterType(this.getClass(), Class1.class, 0);
}


public Class<?> getCType() {
return getTypeParameterType(this.getClass(), Class1.class, 2);
}
}


public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {


public B someB;
public D someD;
public E someE;
}


public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {


public E someE;
}


public static class Class4 extends Class3<Boolean, Long> {


}


public static void test() throws NoSuchFieldException {


Class4 class4 = new Class4();
Class<?> typeA = class4.getAType(); // typeA = Integer
Class<?> typeC = class4.getCType(); // typeC = Long


Field fieldSomeA = class4.getClass().getField("someA");
Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer


Field fieldSomeE = class4.getClass().getField("someE");
Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean




}


/*** UTILS ***/


public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
Map<TypeVariable<?>, Type> subMap = new HashMap<>();
Class<?> superClass;
while ((superClass = subClass.getSuperclass()) != null) {


Map<TypeVariable<?>, Type> superMap = new HashMap<>();
Type superGeneric = subClass.getGenericSuperclass();
if (superGeneric instanceof ParameterizedType) {


TypeVariable<?>[] typeParams = superClass.getTypeParameters();
Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();


for (int i = 0; i < typeParams.length; i++) {
Type actualType = actualTypeArgs[i];
if (actualType instanceof TypeVariable) {
actualType = subMap.get(actualType);
}
if (typeVariable == typeParams[i]) return (Class<?>) actualType;
superMap.put(typeParams[i], actualType);
}
}
subClass = superClass;
subMap = superMap;
}
return null;
}


public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
}


public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
Class<?> type = null;
Type genericType = null;


if (element instanceof Field) {
type = ((Field) element).getType();
genericType = ((Field) element).getGenericType();
} else if (element instanceof Method) {
type = ((Method) element).getReturnType();
genericType = ((Method) element).getGenericReturnType();
}


if (genericType instanceof TypeVariable) {
Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
if (typeVariableType != null) {
type = typeVariableType;
}
}


return type;
}


}

如果你有这样一个类:

public class GenericClass<T> {
private T data;
}

输入T变量,然后输出T名称:

System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.
这是受到Pablo和CoolMind的回答的启发。 偶尔我也会使用kayz1的答案中的技巧(在许多其他答案中也有表达),我相信这是一种体面而可靠的方式来完成OP的要求

我选择首先将其定义为一个接口(类似于PJWeisberg),因为我有可以从该功能中受益的现有类型,特别是异构泛型联合类型:

public interface IGenericType<T>
{
Class<T> getGenericTypeParameterType();
}

我在一个通用匿名接口实现中的简单实现如下所示:

//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
...
private T storedValue = toStore;
...
    

@SuppressWarnings("unchecked")
@Override
public Class<T> getGenericTypeParameterType()
{
return (Class<T>) storedValue.getClass();
}
}
我想这也可以通过用类定义对象作为源来实现,这只是一个单独的用例。 我认为关键在于,正如许多其他答案所述,以某种方式,您需要在运行时获得类型信息,以便在运行时可用;对象本身保持其类型,但是擦除(也如其他人所说,使用适当的引用)会导致任何封闭/容器类型丢失该类型信息

如果你使用的是弹簧:

public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
if (subClass.isSynthetic()) {
return null;
}
return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}

顺便说一下,对于非子类类,GenericTypeResolver仍然会得到null,就像前面提到的问题,因为这样的类的泛型信息在编译后完全是抹去

解决这个问题的唯一方法可能是:

public class GenericClass<T>
{
private final Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz= clazz;
}
    

public Type getMyType()
{
return clazz;
}
}

如果不能更改泛型类并使用本页中已经解释的方法之一,那么简单的方法是根据运行时实例类名获取类型类。

Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
return TypeForClassB.class;
}


//throw an expectation or do whatever you want for the cases not described in the if section.
}

使用返回类类型的抽象方法,然后在该类中使用它,无论在何处扩展泛型类,都必须实现该抽象方法以返回所需的类类型

public class AbsractService<T>{
public abstract Class<T> getClassType ();
.......
}

在运行时

class AnimalService extends AbstractService<MyType>{
    

public Class<MyType> getClassType (){
return MyType.class;
}


.....
}