我有一个泛型类,Foo<T>。在Foo的一个方法中,我想获取T类型的类实例,但我无法调用T.class。

使用T.class绕过它的首选方法是什么?


当前回答

我一直在寻找一种不向类路径添加额外依赖项的方法。经过一些调查,我发现只要你有一个泛型超类型,这是可能的。这对我来说是可以的,因为我正在使用一个具有通用层超类型的DAO层。如果这符合您的情况,那么这是IMHO最整洁的方法。

我遇到的大多数泛型用例都有某种泛型超类型,例如ArrayList<T>的List<T>或DAO<T>的GenericDAO<T>等。

纯Java解决方案

在Java运行时访问泛型类型一文解释了如何使用纯Java实现这一点。

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

弹簧解决方案

我的项目使用的是Spring,它甚至更好,因为Spring有一个方便的实用方法来查找类型。这是对我来说最好的方法,因为它看起来最整洁。我想如果你不使用Spring,你可以编写自己的实用程序方法。

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }

完整代码示例

有些人在评论中很难让这种方法发挥作用,所以我写了一个小应用程序来展示这两种方法。https://github.com/benthurley82/generic-type-resolver-test

其他回答

我根据这个问题中两个最有希望的解决方案之一创建了一个示例。

然而,至少在我的用例中,结果并不那么有希望。

只有一种方法有效,但您需要一个包含方法的超级类,泛型必须在子类中设置,并且不能动态分配(我的用例很遗憾)


import org.junit.jupiter.api.Test;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;


public class GenericTest {

    /**
     * only this will work!
     */
    @Test
    void testGetGenericTypeClassFromChildClassWithSpecifiedType() {
        TestClassWithSpecifiedType parent = new TestClassWithSpecifiedType();
        assertEquals(SomeGenericType.class, parent.getGenericTypeClass());
    }

    /**
     * won't work!
     */
    @Test
    void testGetGenericTypeClassFromChildClassWithUnspecifiedType() {
        TestClassWithUnspecifiedType<SomeGenericType> parent = new TestClassWithUnspecifiedType<>();
        assertThrows(IllegalStateException.class, parent::getGenericTypeClass);
    }

    /**
     * won't work
     */
    @Test
    void testGetGenericTypeClassWithUnspecifiedType() {
        SomeGenericTypedClass<SomeGenericType> parent = new SomeGenericTypedClass<>();
        assertThrows(IllegalStateException.class, parent::getGenericTypeClass);
    }

    /**
     * won't work
     * returns object instead!
     */
    @Test
    void testGetLoadedClassFromObject() {
        Foo<SomeGenericType> foo = new Foo<>();
        Class<?> barClass = foo.getBarClass();
        assertEquals(SomeGenericType.class, barClass);
    }

    /**
     * A class that has specified the type parameter
     */
    public static class TestClassWithSpecifiedType extends AbstractGenericTypedClass<SomeGenericType> {

    }

    /**
     * A class where the type parameter will be specified on demand
     *
     * @param <T>
     */
    public static class TestClassWithUnspecifiedType<T> extends AbstractGenericTypedClass<T> {

    }

    /**
     * An abstract class, because otherwise finding the parameter will not work
     */
    @SuppressWarnings("unchecked")
    public static abstract class AbstractGenericTypedClass<T> {
        @SuppressWarnings("unchecked")
        public 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 <> ");
            }
        }
    }

    /**
     * A typed class without abstract super class
     *
     * @param <T>
     */
    public static class SomeGenericTypedClass<T> {
        @SuppressWarnings("unchecked")
        public 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 <> ");
            }
        }
    }


    /**
     * Some generic type - won't work with primitives such as String, Integer, Double!
     */
    public static class SomeGenericType {

    }

    public static class Foo<T> {
        // The class:
        private final Class<?> barClass;

        public Foo() {
            try {
                // Im giving it [0] cuz Bar is the first TypeParam
                Type[] bounds = getClass().getTypeParameters()[0].getBounds();
                // Here, we get the class now:
                barClass = Class.forName(bounds[0].getTypeName());
            } catch (ClassNotFoundException e) {
                // will never happen!
                throw new Error("Something impossible happened!", e);
            }
        }

        public Class<?> getBarClass() {
            return barClass;
        }
    }
}

我真的不明白为什么这必须如此复杂,但我敢打赌,动态设置参数必须有一些技术限制。

假设您有一个抽象超类,它是泛型的:

public abstract class Foo<? extends T> {}

然后你有了第二个类,它用一个扩展T的通用Bar来扩展Foo:

public class Second extends Foo<Bar> {}

您可以通过选择Type(从bert-bruynoghe答案)并使用class实例推断,在Foo类中获得类Bar.class:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

您必须注意,此操作并不理想,因此最好缓存计算值以避免对此进行多次计算。典型的用途之一是在通用DAO实现中。

最终实施:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

当从其他函数中的Foo类或Bar类调用时,返回的值是Bar.class。

我找到了一种通用而简单的方法。在我的类中,我创建了一个方法,该方法根据泛型类型在类定义中的位置返回泛型类型。让我们假设一个类定义如下:

public class MyClass<A, B, C> {

}

现在,让我们创建一些属性来持久化类型:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

然后,您可以创建一个基于泛型定义的索引返回类型的泛型方法:

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

最后,在构造函数中,只需调用方法并发送每个类型的索引。完整的代码应该如下所示:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}

我正在使用解决方法:

class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();

这是一个有效的解决方案:

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

笔记:只能用作超类

必须使用类型化类进行扩展(Child扩展Generic<Integer>)

OR

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