我有一个泛型类,Foo<T>。在Foo的一个方法中,我想获取T类型的类实例,但我无法调用T.class。
使用T.class绕过它的首选方法是什么?
我有一个泛型类,Foo<T>。在Foo的一个方法中,我想获取T类型的类实例,但我无法调用T.class。
使用T.class绕过它的首选方法是什么?
当前回答
实际上,我想你的类中有一个T型字段。如果没有T型字段,那么有一个泛型类型有什么意义?因此,您可以简单地在该字段上执行实例化。
在我的情况下,我有一个列表<T>项;在我的类中,我检查类类型是否为“Locality”
if (items.get(0) instanceof Locality) ...
当然,这只在可能的类总数有限的情况下有效。
其他回答
正如其他答案中所解释的,要使用这种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。
我根据这个问题中两个最有希望的解决方案之一创建了一个示例。
然而,至少在我的用例中,结果并不那么有希望。
只有一种方法有效,但您需要一个包含方法的超级类,泛型必须在子类中设置,并且不能动态分配(我的用例很遗憾)
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;
}
}
}
我真的不明白为什么这必须如此复杂,但我敢打赌,动态设置参数必须有一些技术限制。
然而,有一个小漏洞:如果您将Foo类定义为抽象类。这意味着您必须将类实例化为:
Foo<MyType> myFoo = new Foo<MyType>(){};
(注意末端的双撑杆。)
现在,您可以在运行时检索T的类型:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
然而,请注意,mySuperclass必须是实际定义T的最终类型的类定义的超类。
它也不是很优雅,但你必须决定你喜欢新的Foo<MyType>(){}还是新的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 );
}
}
这是一个有效的解决方案:
@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扩展Generic<Integer>)
OR
必须创建为匿名实现(new Generic<Integer>(){};)