How to get a class instance of generics type T
我有一个仿制药班,
使用
简短的答案是,没有办法在Java中找出泛型类型参数的运行时类型。我建议在Java教程中阅读关于擦除的章节以获得更多的细节。
对此,一种流行的解决方案是将类型参数的
1 2 3 4 5 6 7 8 9 10 11 | 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 } } |
我在寻找一种不增加对类路径的额外依赖性的方法。经过一些调查,我发现只要有一个泛型父类型,就有可能。这对我来说没问题,因为我正在使用一个带有通用层超类型的DAO层。如果这符合你的情况,那就是最整洁的方法。
我遇到的大多数泛型用例都有某种泛型超类型,例如,
在Java中运行时访问泛型类型的文章解释了如何使用纯Java来实现它。
弹簧解我的项目使用的是Spring,它甚至更好,因为Spring有一个查找类型的简便实用方法。这对我来说是最好的方法,因为它看起来很整洁。我想如果你不使用Spring,你可以编写自己的实用方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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"; } |
不过,还有一个小漏洞:如果您将
1 | Foo<MyType> myFoo = new Foo<MyType>(){}; |
(注意两端的双大括号。)
现在,您可以在运行时检索
1 2 | Type mySuperclass = myFoo.getClass().getGenericSuperclass(); Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0]; |
但是请注意,
它也不是很优雅,但是您必须在代码中决定您是喜欢
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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; } } } |
然后:
1 2 3 4 5 6 7 8 9 10 11 |
标准方法/解决方案/解决方案是向构造函数添加一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); } } |
假设您有一个通用的抽象超类:
1 | public abstract class Foo<? extends T> {} |
然后有第二个类用扩展t的泛型条扩展foo:
1 | public class Second extends Foo<Bar> {} |
您可以在foo类中选择
1 2 3 4 5 | 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实现中。
最终实施:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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。
以下是一个有效的解决方案:
1 2 3 4 5 6 7 8 9 10 | @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 <>"); } } |
笔记:只能用作超类
或
你不能这样做,因为类型删除。参见堆栈溢出问题Java泛型-类型擦除-什么时候发生什么。
我在抽象泛型类中遇到了这个问题。在这种特殊情况下,解决方案更简单:
1 2 3 4 | abstract class Foo<T> { abstract Class<T> getTClass(); //... } |
在派生类的后面:
1 2 3 4 5 6 | class Bar extends Foo<Whatever> { @Override Class<T> getTClass() { return Whatever.class; } } |
一个比其他人建议的类更好的路由是传入一个对象,该对象可以执行您对类所做的操作,例如创建一个新实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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>()); |
对于这个问题,我有一个(丑陋但有效的)解决方案,我最近使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 __() { } } |
这是可能的:
1 2 3 | class Foo<T> { Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0); } |
您需要从Sv/Base/Daa/Src/Ma/Java/COM/GooGeCord/GuangiDaa/Daou/DaouTur.java两个函数。
有关更多解释,请参见反射泛型。
我找到了一个通用的简单方法。在我的类中,我创建了一个方法,该方法根据泛型类型在类定义中的位置返回它。让我们假设一个这样的类定义:
1 2 3 | public class MyClass<A, B, C> { } |
现在,让我们创建一些属性来持久化这些类型:
1 2 3 4 5 6 7 8 9 10 11 | public class MyClass<A, B, C> { private Class<A> aType; private Class bType; private Class<C> cType; // Getters and setters (not necessary if you are going to use them internally) } |
然后,可以创建一个基于泛型定义索引返回类型的泛型方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /** * 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]; } |
最后,在构造函数中,只需调用方法并发送每种类型的索引。完整的代码应该如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class MyClass<A, B, C> { private Class<A> aType; private Class bType; private Class<C> cType; public MyClass() { this.aType = (Class<A>) getGenericClassType(0); this.bType = (Class) 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]; } } |
如其他答案所述,要使用这种
因此,使类抽象化它迫使您扩展它,从而满足子类化的要求。(使用Lombok的@getter)。
1 2 3 4 5 6 7 8 9 10 11 | @Getter public abstract class ConfigurationDefinition<T> { private Class<T> type; ... public ConfigurationDefinition(...) { this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; ... } } |
现在扩展它而不定义新类。(注意结尾的…扩展,但不要覆盖任何内容-除非您愿意)。
1 2 3 4 5 | private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){}; private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){}; ... Class stringType = myConfigA.getType(); Class fileType = myConfigB.getType(); |
1 2 3 4 5 6 7 8 9 10 11 | public <T> T yourMethodSignature(Class<T> type) { // get some object and check the type match the given type Object result = ... if (type.isAssignableFrom(result.getClass())) { return (T)result; } else { // handle the error } } |
如果您正在扩展或实现任何使用泛型的类/接口,那么您可以获得父类/接口的泛型类型,而不必修改任何现有的类/接口。
可能有三种可能性,
案例1当类扩展使用泛型的类时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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当类正在实现使用泛型的接口时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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当接口扩展使用泛型的接口时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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> { } |
实际上,我认为在T类型的类中有一个字段。如果没有T类型的字段,那么拥有一个泛型类型有什么意义呢?所以,您可以简单地在该字段上执行一个实例。
在我的例子中,我在我的班级里有一个
1 | List<T> items; |
,我通过以下方式检查班级类型是否为"地区"。
1 | if (items.get(0) instanceof Locality) ... |
当然,只有在可能类的总数有限的情况下,这才有效。
我正在为此使用变通方法:
1 2 3 4 5 | class MyClass extends Foo<T> { .... } MyClass myClassInstance = MyClass.class.newInstance(); |