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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3403909/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 22:59:24  来源:igfitidea点击:

Get generic type of class at runtime

javagenericsreflection

提问by Glenn

How can I achieve this?

我怎样才能做到这一点?

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

Everything I have tried so far always returns type Objectrather than the specific type used.

到目前为止,我尝试过的所有方法都返回类型Object而不是使用的特定类型。

回答by Dimitar

I dont think you can, Java uses type erasure when compiling so your code is compatible with applications and libraries that were created pre-generics.

我不认为您可以,Java 在编译时使用类型擦除,因此您的代码与预先创建的应用程序和库兼容。

From the Oracle Docs:

来自 Oracle 文档:

Type Erasure

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

类型擦除

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

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

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

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

回答by andrewmu

You can't. If you add a member variable of type T to the class (you don't even have to initialise it), you could use that to recover the type.

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

回答by ewernli

Generics are not reifiedat run-time. This means the information is not present at run-time.

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

Adding generics to Java while mantaining backward compatibility was a tour-de-force (you can see the seminal paper about it: Making the future safe for the past: adding genericity to the Java programming language).

在保持向后兼容性的同时将泛型添加到 Java 是一种绝妙的做法(您可以查看有关它的开创性论文:让未来安全过去:向 Java 编程语言添加泛型)。

There is a rich literature on the subject, and some people are dissatisfiedwith the current state, some says that actually it's a lureand there is no real need for it. You can read both links, I found them quite interesting.

关于这个主题有丰富的文献,有些人现状不满意,有些人说这实际上是一种诱惑,并没有真正的必要。你可以阅读这两个链接,我发现它们很有趣。

回答by FrVaBe

I have seen something like this

我见过这样的

private Class<T> persistentClass;

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

in the hibernate GenericDataAccessObjectsExample

休眠 GenericDataAccessObjects示例中

回答by Henning

As others mentioned, it's only possible via reflection in certain circumstances.

正如其他人所提到的,只有在某些情况下才能通过反射来实现。

If you really need the type, this is the usual (type-safe) workaround pattern:

如果您确实需要该类型,这是通常的(类型安全)解决方法模式:

public class GenericClass<T> {

     private final Class<T> type;

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

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

回答by josefx

Java generics are mostly compile time, this means that the type information is lost at runtime.

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

class GenericCls<T>
{
    T t;
}

will be compiled to something like

将被编译为类似

class GenericCls
{
   Object o;
}

To get the type information at runtime you have to add it as an argument of the ctor.

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

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

Example:

例子:

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

回答by Joeri Hendrickx

Sure, you can.

你当然可以。

Java does not usethe information at run time, for backwards compatibility reasons. But the information is actually presentas metadata and can be accessed via reflection (but it is still not used for type-checking).

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

From the official API:

来自官方API:

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

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

However, for your scenario I would not use reflection. I'm personally more inclined to use that for framework code. In your case I would just add the type as a constructor param.

但是,对于您的场景,我不会使用反射。我个人更倾向于将其用于框架代码。在您的情况下,我只会将类型添加为构造函数参数。

回答by Matthias M

This is my solution:

这是我的解决方案:

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

回答by Ondrej Bozek

Technique described in this article by Ian Robertsonworks for me.

Ian Robertson在这篇文章中描述的技术对我有用

In short quick and dirty example:

简而言之,快速而肮脏的例子:

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

回答by PJWeisberg

Here's one way, which I've had to use once or twice:

这是一种方法,我不得不使用一两次:

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

Along with

随着

public class SpecificClass extends GenericClass<String>{

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