我有一个泛型类,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;
        }
    }
}

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

其他回答

正如其他答案中所解释的,要使用这种ParameterizedType方法,您需要扩展该类,但创建一个扩展它的新类似乎需要额外的工作。。。

因此,使类抽象化会迫使您扩展它,从而满足子类化要求。(使用lombok的@Getter)。

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

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

现在在不定义新类的情况下扩展它。(请注意末尾的{}…扩展,但不要覆盖任何内容,除非您愿意)。

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();

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

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。

我假设,既然你有一个泛型类,你会有一个这样的变量:

private T t;

(此变量需要在构造函数中取值)

在这种情况下,您可以简单地创建以下方法:

Class<T> getClassOfInstance()
{
    return (Class<T>) t.getClass();
}

希望它有帮助!

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

可能有三种可能性,

案例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> {
}

这个问题由来已久,但现在最好的办法是使用谷歌Gson。

获取自定义视图模型的示例。

Class<CustomViewModel<String>> clazz = new GenericClass<CustomViewModel<String>>().getRawType();
CustomViewModel<String> viewModel = viewModelProvider.get(clazz);

泛型类型类

class GenericClass<T>(private val rawType: Class<*>) {

    constructor():this(`$Gson$Types`.getRawType(object : TypeToken<T>() {}.getType()))

    fun getRawType(): Class<T> {
        return rawType as Class<T>
    }
}