如何获取泛型类型T的类实例?

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

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

911712 次浏览

由于类型擦除,您无法执行此操作。另请参阅Stack Overflow问题Java泛型-类型擦除-何时发生以及发生什么

简短的回答是,没有办法在Java中找出泛型类型参数的运行时类型。我建议阅读Java教程中关于类型擦除的章节以了解更多详细信息。

一个流行的解决方案是将类型参数的Class传递给泛型类型的构造函数,例如。

class Foo<T> {final Class<T> typeParameterClass;
public Foo(Class<T> typeParameterClass) {this.typeParameterClass = typeParameterClass;}
public void bar() {// you can access the typeParameterClass here and do whatever you like}}

一个标准的方法/变通方法/解决方案是将class对象添加到构造函数中,例如:

 public class Foo<T> {
private Class<T> type;public Foo(Class<T> type) {this.type = type;}
public Class<T> getType() {return type;}
public T newInstance() {return type.newInstance();}}

比其他人建议的Class更好的途径是传入一个可以执行您对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>());

但是有一个小漏洞:如果您将Foo类定义为抽象。这意味着你必须实例化你的类:

Foo<MyType> myFoo = new Foo<MyType>(){};

(注意最后的双括号。

现在您可以在运行时检索T的类型:

Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

但是请注意,mySuperclass必须是实际定义T的最终类型的类定义的超类。

它也不是很优雅,但您必须决定在代码中选择new Foo<MyType>(){}还是new Foo<MyType>(MyType.class);


例如:

import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;
import java.util.ArrayDeque;import java.util.Deque;import java.util.NoSuchElementException;
/*** Captures and silently ignores stack exceptions upon popping.*/public abstract class SilentStack<E> extends ArrayDeque<E> {public E pop() {try {return super.pop();}catch( NoSuchElementException nsee ) {return create();}}
public E create() {try {Type sooper = getClass().getGenericSuperclass();Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];
return (E)(Class.forName( t.toString() ).newInstance());}catch( Exception e ) {return null;}}}

然后:

public class Main {// Note the braces...private Deque<String> stack = new SilentStack<String>(){};
public static void main( String args[] ) {// Returns a new instance of String.String s = stack.pop();System.out.printf( "s = '%s'\n", s );}}

实际上,我假设你的类中有一个类型为T的字段。如果没有类型为T的字段,那么拥有泛型Type的意义是什么?所以,你可以简单地对该字段做一个instance of。

在我的例子中,我的类中有一个

列出项;
,我检查类类型是否为“Locality”

if (items.get(0) instanceof Locality) ...

当然,这只有在可能的类总数有限的情况下才有效。

我一直在寻找一种在不向类路径添加额外依赖项的情况下自己执行此操作的方法。经过一些调查,我发现只要你有一个泛型超类型,它就可以<强>是。这对我来说没问题,因为我正在使用具有泛型层超类型的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有一个方便的实用程序方法来查找类型。这对我来说是最好的方法,因为它看起来最整洁。我想如果你不使用Spring,你可以编写自己的实用程序方法。

import org.springframework.core.GenericTypeResolver;
public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>{
@Autowiredprivate 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

我在抽象泛型类中遇到了这个问题。在这个特殊情况下,解决方案更简单:

abstract class Foo<T> {abstract Class<T> getTClass();//...}

稍后在派生类上:

class Bar extends Foo<Whatever> {@OverrideClass<T> getTClass() {return Whatever.class;}}

这是可能的:

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

您需要hibernate-通用道/blob/主/道/src/main/java/com/googlecode/通用道/道/DAOUtil.java中的两个函数。

有关更多解释,请参阅反映泛型

假设你有一个泛型的抽象超类:

public abstract class Foo<? extends T> {}

然后你有第二个类,它用一个扩展T的泛型Bar扩展Foo:

public class Second extends Foo<Bar> {}

您可以通过选择Type(来自bert Bruynooghes答案)并使用Class实例来获取Foo类中的类Bar.class

Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];//Parse it as StringString 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 <T> T yourMethodSignature(Class<T> type) {
// get some object and check the type match the given typeObject result = ...
if (type.isAssignableFrom(result.getClass())) {return (T)result;} else {// handle the error}}

这里有一个工作解决方案:

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

注意:只能用作超类

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

或者

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

如果您正在扩展或实现任何使用泛型的类/接口,您可以获得父类/接口的泛型类型,而无需修改任何现有的类/接口。

可能有三种可能性,

案例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当您的接口扩展使用泛型的接口时

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

我正在为此使用变通方法:

class MyClass extends Foo<T> {....}
MyClass myClassInstance = MyClass.class.newInstance();

正如在其他答案中解释的那样,要使用这种ParameterizedType方法,您需要扩展类,但这似乎是额外的工作来创建一个扩展它的全新类…

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

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

实际上,它可能(没有外部库!)

以下是我对这个问题的(丑陋但有效的)解决方案:

import java.lang.reflect.TypeVariable;

public static <T> Class<T> getGenericClass() {__<T> instance = new __<T>();TypeVariable<?>[] parameters = instance.getClass().getTypeParameters();
return (Class<T>)parameters[0].getClass();}
// Generic helper class which (only) provides type information. This avoids the//   usage of a local variable of type T, which would have to be initialized.private final class __<T> {private __() { }}

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

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

这个问题很老,但现在最好的是使用谷歌Gson

获取自定义viewModel的示例。

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

这是非常简单的。如果您需要在同一个类中:

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

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

private T t;

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

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

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

希望有帮助!

我想把T.class传递给一个使用泛型的方法

方法读取文件读取一个由fileName指定的带有fullpath的. csv文件。可以有具有不同内容的csv文件,因此我需要传递模型文件类,以便我可以获得适当的对象。由于这是读取csv文件,我想以通用方式进行。出于某种原因,上述解决方案都不适合我。我需要使用Class<? extends T> type使其工作。我使用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 readerCsvToBean<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);

很多人都不知道这个技巧!实际上,我今天才发现它!它就像一个梦!看看这个例子:

public static void main(String[] args) {Date d=new Date();  //Or anything you want!printMethods(d);}
public static <T> void printMethods(T t){Class<T> clazz= (Class<T>) t.getClass(); // There you go!for ( Method m : clazz.getMethods()){System.out.println( m.getName() );}}

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

然而,结果是不太有希望,至少对于我的用例来说。

只有一个方法有效,但是包含方法的需要一个超级阶级仿制药必须是设置在子类中不能动态赋值(我的用例很遗憾)

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!*/@Testvoid testGetGenericTypeClassFromChildClassWithSpecifiedType() {TestClassWithSpecifiedType parent = new TestClassWithSpecifiedType();assertEquals(SomeGenericType.class, parent.getGenericTypeClass());}
/*** won't work!*/@Testvoid testGetGenericTypeClassFromChildClassWithUnspecifiedType() {TestClassWithUnspecifiedType<SomeGenericType> parent = new TestClassWithUnspecifiedType<>();assertThrows(IllegalStateException.class, parent::getGenericTypeClass);}
/*** won't work*/@Testvoid testGetGenericTypeClassWithUnspecifiedType() {SomeGenericTypedClass<SomeGenericType> parent = new SomeGenericTypedClass<>();assertThrows(IllegalStateException.class, parent::getGenericTypeClass);}
/*** won't work* returns object instead!*/@Testvoid 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 TypeParamType[] 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;}}}

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