在运行时获取泛型类

Gle*_*enn 474 java generics reflection

我怎样才能做到这一点?

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

到目前为止我尝试的所有东西总是返回类型Object而不是使用的特定类型.

Hen*_*ing 299

正如其他人所提到的那样,只有在某些情况下通过反思才有可能.

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

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个答案,但实例化它有点麻烦:GenericClass <AnotherClass> g = new GenericClass <AnotherClass>(AnotherClass.class); (57认同)
  • 您可以通过提供一个通用的静态工厂方法避开多余的参考.像(public <T> type){...}`的`public static <T> GenericClass <T>之类的东西然后调用它:`GenericClass <String> var = GenericClass.of(String.class)`.好一点. (15认同)
  • 就像我之前的评论的后续行动一样 - 经过反思后很多痛苦,我最终使用了这个答案. (5认同)
  • 这是真的,但并不适用于所有情况,例如无状态远程bean,它们通过容器/反射进行实例化. (2认同)

FrV*_*aBe 244

我见过这样的事情

private Class<T> persistentClass;

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

hibernate GenericDataAccessObjects示例中

  • 此技术适用于在直接超类上定义类型参数的情况,但如果类型参数在类型层次结构中的其他位置定义,则该方法将失败.对于处理更复杂的情况,可以使用[TypeTools](https://github.com/jhalterman/typetools)之类的东西.文档包括更复杂的通用DAO的示例. (81认同)
  • 我得到`java.lang.Class无法转换为java.lang.reflect.ParameterizedType`这个答案. (29认同)
  • 这仅返回_CLASS_实现/扩展具有泛型声明的内容时使用的实际类型参数,它不返回实例化_INSTANCE_时使用的实际类型参数.换句话说,它**CAN**告诉你在`A类实现Comparable <String>`中,实际的类型参数是`String`,但它不能**在`Set <String>中告诉它a = new TreeSet <String>()`,实际的类型参数是`String`.事实上,类型参数信息在编译后被"擦除",如其他答案中所解释的. (24认同)
  • @TomášZato简单地调用上面的代码为我返回了相同的例外.我知道它有点晚了,但无论如何,在我的情况下,我不得不调用`(Class <T>)((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()`来实现类型参数. (5认同)
  • 这种方法也可以使用杰克逊人的"Class-Mate"来实现.我在这里写了一个要点https://gist.github.com/yunspace/930d4d40a787a1f6a7d1 (3认同)

ewe*_*nli 91

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

在保留向后兼容性的同时向Java中添加泛型是一种强制游戏(你可以看到关于它的开创性论文:为过去创造未来安全:为Java编程语言添加通用性).

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

  • 当然我们不满意,.NET有更好的通用处理机制 (159认同)
  • Java 类型擦除是一个历史设计缺陷;为了解决这个问题而编写的代码比为了实现它而编写的代码还要多。 (7认同)
  • @Pacerier:但仅凭具体化的泛型不会将Java带到.NET的水平.值类型一个专门的代码对于为什么.NET在泛型领域更好是至关重要的. (6认同)
  • @ spaaarky21不,在编译期间删除泛型类型参数(所谓的"擦除",你可以google它).FrVaBe的答案中的技巧只有在超类的类型参数静态知道时才有效(参见Johnathn的第一篇文章) (3认同)

Cod*_*Ray 62

使用番石榴.

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
  }
}
Run Code Online (Sandbox Code Playgroud)

前阵子,我贴了一些功能完善的例子包括抽象类和子类在这里.

注意:这要求您实例化一个子类,GenericClass以便它可以正确绑定类型参数.否则它只会返回类型T.

  • 请注意,我创建了一个空的匿名子类(请参阅末尾的两个花括号).这使用反射来对抗Java的运行时类型擦除.您可以在此处了解详情:https://code.google.com/p/guava-libraries/wiki/ReflectionExplained (3认同)
  • @ CodyA.Ray你的代码抛出一个`java.lang.IllegalArgumentException:类com.google.common.reflect.TypeToken没有参数化`.所以我将`new TypeToken(getClass()){}`改为`new TypeToken <T>(getClass()){}`.现在,代码运行正常,但Type仍然是'T'.见:https://gist.github.com/m-manu/9cda9d8f9d53bead2035 (3认同)
  • @Dominik请参阅更新的示例,您可以复制并粘贴以自行测试.我还添加了一条说明,说明您必须实例化一个子类(如图所示).作为一般的礼仪建议,请在您指责"一厢情愿"的海报之前阅读任何链接的文章和相关的javadoc.我多次使用类似的生产代码.我正在演示的番石榴助手是针对这个确切的用例,他们的javadocs显示了这个问题的几乎确切的答案.http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/reflect/TypeToken.html (3认同)
  • 构造函数 TypeToken(Type) 不可见 (2认同)
  • @ CodyA.Ray因为这仅适用于GenericClass的子类,所以您应该使该类为abstract,这样不会编译错误的用法。 (2认同)
  • ReflectionExplained 的 URL 已更改,这是当前的 url https://github.com/google/guava/wiki/ReflectionExplained (2认同)

Joe*_*ckx 37

你当然可以.

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

从官方API:

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

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

  • getActualTypeArguments仅返回直接类的类型参数.如果您有一个复杂的类型层次结构,其中T可以在层次结构中的任何位置进行参数化,那么您需要做一些工作来弄清楚它是什么.这或多或少是[TypeTools](https://github.com/jhalterman/typetools)的作用. (10认同)

jos*_*efx 34

Java泛型主要是编译时,这意味着类型信息在运行时丢失.

class GenericCls<T>
{
    T t;
}
Run Code Online (Sandbox Code Playgroud)

将编译成类似的东西

class GenericCls
{
   Object o;
}
Run Code Online (Sandbox Code Playgroud)

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

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}
Run Code Online (Sandbox Code Playgroud)

例:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
Run Code Online (Sandbox Code Playgroud)

  • `private final Class <T> type;` (7认同)

Moe*_*sio 21

我使用了以下方法:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我收到带有此示例代码的“线程“主”中的异常java.lang.ClassCastException:java.lang.Class无法转换为java.lang.reflect.ParameterizedType” (3认同)

Dim*_*tar 15

我认为你不能,Java在编译时使用类型擦除,因此你的代码与仿制前创建的应用程序和库兼容.

来自Oracle Docs:

类型擦除

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

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

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

  • 这是完全可能的,请参阅其他多个答案。 (2认同)

Ond*_*zek 15

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;
    }
  }
Run Code Online (Sandbox Code Playgroud)

  • 此代码中的哪一行是实际的,正在读取运行时类型参数? (3认同)

Ali*_*neh 15

public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

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

  • 请注意,只有拥有通用类型的类为ABSTRACT时,此解决方案才有效 (3认同)
  • 我赞成这个答案,因为它是一种适用于所问问题的解决方案。但是,对于那些想要像我一样在一个类层次结构中向上导航的人,有一个以上的Generic类,这是行不通的。因为您将获得java.lang.object而不是实际的类。 (2认同)

DaB*_*ick 9

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

你想要做的是(安全地)将通用类型参数的类型从concerete类"传递"到超类.

如果您允许自己将类类型视为类中的"元数据",则表明在运行时编码元数据的Java方法:注释.

首先在这些行中定义自定义注释:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}
Run Code Online (Sandbox Code Playgroud)

然后,您必须将注释添加到子类中.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此代码获取基类中的类类型:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}
Run Code Online (Sandbox Code Playgroud)

这种方法的一些局限性是:

  1. PassedGenericType在两个位置指定泛型类型()而不是非DRY的一个.
  2. 只有在可以修改具体子类时才可以执行此操作.


Mat*_*s M 8

这是我的解决方案:

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);
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是这个问题的答案. (9认同)

小智 7

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

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}
Run Code Online (Sandbox Code Playgroud)

随着

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这在技术上是有效的,但是它不能解决一般情况,我认为这就是原始海报所追求的。 (4认同)

Yar*_*bas 6

这是工作解决方案!

@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 <> ");
        }
    } 
Run Code Online (Sandbox Code Playgroud)

注意: 只能用作超类
1.必须使用类型化的类扩展(Child extends Generic<Integer>)

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

  • 类转换异常 (2认同)

小智 6

这个驾驶室的一个简单解决方案如下

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这需要传递一个对象,但这并不总是可行的。 (3认同)

and*_*wmu 5

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

  • 哎呀,好的。你*确实*必须从构造函数初始化它。 (3认同)