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

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


当前回答

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

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

其他回答

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

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

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


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。

你不能这样做,因为类型删除。另请参阅堆栈溢出问题Java泛型-类型擦除-何时发生以及发生什么。

我想将T.class传递给一个使用Generics的方法

readFile方法读取文件名指定的.csv文件,文件名为fullpath。可以有不同内容的csv文件,因此我需要传递模型文件类,以便获得适当的对象。因为这是读取csv文件,所以我想以通用的方式进行。由于某种原因或其他原因,上述解决方案都不适用于我。我需要使用类别<?扩展T>类型以使其工作。我使用opencsv库解析CSV文件。

private <T>List<T> readFile(String fileName, Class<? extends T> type) {

    List<T> dataList = new ArrayList<T>();
    try {
        File file = new File(fileName);

        Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
        Reader headerReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

        CSVReader csvReader = new CSVReader(headerReader);
        // create csv bean reader
        CsvToBean<T> csvToBean = new CsvToBeanBuilder(reader)
                .withType(type)
                .withIgnoreLeadingWhiteSpace(true)
                .build();

        dataList = csvToBean.parse();
    }
    catch (Exception ex) {
        logger.error("Error: ", ex);
    }

    return dataList;
}

这是readFile方法的调用方式

List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);

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

可能有三种可能性,

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