我有一个泛型类Foo<T>
。在一种方法中Foo
,我想获取type的类实例T
,但是我无法调用T.class
。
使用它解决问题的首选方法是什么T.class
?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
我有一个泛型类Foo<T>
。在一种方法中Foo
,我想获取type的类实例T
,但是我无法调用T.class
。
使用它解决问题的首选方法是什么T.class
?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
Answers:
简短的答案是,无法找到Java中泛型类型参数的运行时类型。我建议阅读Java教程中有关类型擦除的章节以获取更多详细信息。
一个流行的解决方案是Class
将type参数的传递给泛型类型的构造函数,例如
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
}
}
typeParameterClass
在构造函数中分配没有默认分配的完全可以。无需再次设置。
我一直在寻找一种自己做的方法,而又不对类路径添加额外的依赖。经过一番调查,我发现,这是可能的,只要你有一个通用的超类型。这对我来说是可以的,因为我正在使用具有通用层超类型的DAO层。如果这符合您的情况,那是最整洁的方法恕我直言。
我遇到的大多数泛型用例都有某种泛型超类型,例如List<T>
for ArrayList<T>
或GenericDAO<T>
for DAO<T>
,等等。
在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 ";
}
但是存在一个小漏洞:如果您将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 );
}
}
TypeLiteral
a
和b
创建这种方式都将扩展同一类,但不具有相同的实例类。 a.getClass() != b.getClass()
一种标准的方法/解决方法/解决方案是将一个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();
}
}
假设您有一个通用的抽象超类:
public abstract class Foo<? extends T> {}
然后是第二个类,它使用扩展T的通用Bar扩展了Foo:
public class Second extends Foo<Bar> {}
您可以Bar.class
通过选择Type
(来自bert bruynooghe答案)并使用Class
实例进行推断来获得Foo 类中的类:
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。
toString().split(" ")[1]
那就是问题,请避免"class "
这是一个可行的解决方案:
@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 extends Generic<Integer>
)进行扩展要么
new Generic<Integer>() {};
) 由于类型擦除,您无法执行此操作。另请参见堆栈溢出问题Java泛型-擦除-何时以及发生什么情况。
一个比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>());
我在抽象的泛型类中遇到了这个问题。在这种情况下,解决方案更简单:
abstract class Foo<T> {
abstract Class<T> getTClass();
//...
}
然后在派生类上:
class Bar extends Foo<Whatever> {
@Override
Class<T> getTClass() {
return Whatever.class;
}
}
我对此问题有一个(难看但有效的)解决方案,我最近使用了它:
import java.lang.reflect.TypeVariable;
public static <T> Class<T> getGenericClass()
{
__<T> ins = new __<T>();
TypeVariable<?>[] cls = ins.getClass().getTypeParameters();
return (Class<T>)cls[0].getClass();
}
private final class __<T> // generic helper class which does only provide type information
{
private __()
{
}
}
这是可能的:
class Foo<T> {
Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}
有关更多说明,请参见反映泛型。
我找到了一种通用且简单的方法。在我的课程中,我创建了一个方法,该方法根据其在类定义中的位置返回泛型。让我们假设这样的类定义:
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];
}
}
正如其他答案所解释的那样,要使用这种ParameterizedType
方法,您需要扩展该类,但这似乎是做一个额外的工作来制作一个扩展该类的全新类...
因此,将类抽象化将迫使您对其进行扩展,从而满足子类化要求。(使用龙目岛的@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();
如果要扩展或实现任何使用泛型的类/接口,则可以获取父类/接口的泛型类型,而无需修改任何现有的类/接口。
可能有三种可能性,
情况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 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() );
}
实际上,我想您的类中有一个类型为T的字段。如果没有类型为T的字段,那么拥有通用类型有什么意义呢?因此,您只需在该字段上执行instanceof。
就我而言,我有一个
列出<T>项;在我的班级中,然后检查班级类型是否为“本地化”
如果(items.get(0)instanceof Locality)...
当然,这仅在可能的类别总数有限的情况下有效。
这个问题很旧,但现在最好是使用google Gson
。
获取custom的示例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>
}
}
我想将T.class传递给使用泛型的方法
readFile方法读取具有全路径的fileName指定的.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 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);