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

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


当前回答

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

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

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


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

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

其他回答

有可能:

class Foo<T> {
  Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}

您需要两个来自hibernate generic dao/blob/master/dao/src/main/java/com/googlecode/generic dao/dao/DAOUtil.java的函数。

有关更多说明,请参阅反射泛型。

如果要扩展或实现任何使用泛型的类/接口,则可以获得父类/接口的泛型类型,而不必修改任何现有类/接口。

可能有三种可能性,

案例1当您的类正在扩展使用泛型的类时

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

案例2当类实现使用泛型的接口时

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

案例3当您的接口扩展使用Generics的接口时

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}

比其他人建议的类更好的方法是传入一个对象,该对象可以执行您对类所做的操作,例如创建一个新实例。

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());

这很直接。如果您需要来自同一类:

Class clazz = this.getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
try {
        Class typeClass = Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName() );
        // You have the instance of type 'T' in typeClass variable

        System.out.println( "Class instance name: "+  typeClass.getName() );
    } catch (ClassNotFoundException e) {
        System.out.println( "ClassNotFound!! Something wrong! "+ e.getMessage() );
    }

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

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