我有一个类Foo<T, U>
,其中包含以下构造函数:
public Foo() {
clazz = Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
}
我在构造函数中做的是获取参数U
的类。我需要它,因为我用它来实例化那个类。
问题是当我有一个Foo
的子类不是它的直接次级时,它不起作用。让我举一个例子。
我有班级Bar<T> extends Foo<T, Class1>
。在这里,Class1
不是变量而是类。
我也有班级Baz extends Bar<Class2>
。 Class2
也是一个类,而不是变量。
问题是当我尝试实例化Baz
(Baz -> Bar<Class2> -> Foo<T, Class2>
)时它失败了。我得到一个ArrayIndexOutOfBoundsException
,因为getActualTypeArguments()
返回一个只包含类Class2
(大小为1)的数组,而我正在尝试获取数组的第二个元素。那是因为我得到了Bar<Class2>
的论据,而不是Foo的论据。
我想要的是以某种方式修改Foo构造函数我可以在参数U中获取类,如果我实例化的类是否是直接子类则无关紧要。我想我应该可以进入类的层次结构,直到达到类Foo,将其转换为ParameterizedType并获取参数,但我找不到。
有什么想法吗?
提前致谢。
答案 0 :(得分:3)
这是一个很好的方法 - 它是实际获取在Java字节码中编译的泛型类型信息的唯一方法。 Java中泛型的其他东西只是类型擦除。见下文似乎是一个有效的解决方案。有四种情况:
clazz
将为null
)clazz = null
)这是代码(Test.java):
import java.lang.reflect.*;
import java.util.*;
class A<T1, T2>
{
Class<T2> clazz;
A()
{
Type sc = getClass().getGenericSuperclass();
Map<TypeVariable<?>, Class<?>> map = new HashMap<TypeVariable<?>, Class<?>>();
while (sc != null)
{
if (sc instanceof ParameterizedType)
{
ParameterizedType pt = (ParameterizedType) sc;
Type[] ata = pt.getActualTypeArguments();
TypeVariable[] tps = ((Class) pt.getRawType())
.getTypeParameters();
for (int i = 0; i < tps.length; i++)
{
Class<?> value;
if (ata[i] instanceof TypeVariable)
{
value = map.get(ata[i]);
}
else
{
value = (Class) ata[i];
}
map.put(tps[i], value);
}
if (pt.getRawType() == A.class)
{
break;
}
if (ata.length >= 1)
{
sc = ((Class) pt.getRawType()).getGenericSuperclass();
}
}
else
{
sc = ((Class) sc).getGenericSuperclass();
}
}
TypeVariable<?> myVar = A.class.getTypeParameters()[1];
clazz = map.containsKey(myVar) ? (Class<T2>) map.get(myVar) : null;
}
}
class Bar<T> extends A<T, String> {}
class Baz extends Bar<Integer> {}
class A2<T3, T1, T2> extends A<T1, T2> { }
class B<T> extends A2<Long, String, T> { }
class C extends B<Integer> { }
class D extends C { }
class Plain<T1, T2> extends A<T1, T2> {}
public class Test
{
public static void main(String[] args)
{
new D();
new Baz();
new A<String, Integer>();
new Plain<String, Integer>();
}
}
答案 1 :(得分:3)
我创建了一个方法,它将返回一个参数化泛型的类型数组,其中'null'表示仍然存在的参数。我针对您的Foo层次结构测试了它,以及使用类'A'的其他答案中提供的示例。
编辑:在我没有考虑重新排序类型会如何影响绑定之前。在新方法中,传入您想要参数和基类的超类。当我迭代到超类时,我通过比较参数名称来收集类的参数,并从前一个基类向前映射。我希望这很清楚。
public <S, B extends S> Class[] findTypeParameters(Class<B> base, Class<S> superClass) {
Class[] actuals = new Class[0];
for (Class clazz = base; !clazz.equals(superClass); clazz = clazz.getSuperclass()) {
if (!(clazz.getGenericSuperclass() instanceof ParameterizedType))
continue;
Type[] types = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
Class[] nextActuals = new Class[types.length];
for (int i = 0; i < types.length; i++)
if (types[i] instanceof Class)
nextActuals[i] = (Class) types[i];
else
nextActuals[i] = map(clazz.getTypeParameters(), types[i], actuals);
actuals = nextActuals;
}
return actuals;
}
private Class map(Object[] variables, Object variable, Class[] actuals) {
for (int i = 0; i < variables.length && i < actuals.length; i++)
if (variables[i].equals(variable))
return actuals[i];
return null;
}
@Test
public void findsTypeOfSuperclass() throws Exception {
assertThat(findTypeParameters(A4.class, A1.class), arrayContaining(Integer.class, Boolean.class, String.class));
assertThat(findTypeParameters(B4.class, A1.class), arrayContaining(Integer.class, Boolean.class, String.class));
assertThat(findTypeParameters(C2.class, A1.class), arrayContaining(Integer.class, null, null));
assertThat(findTypeParameters(D4.class, A1.class), arrayContaining(Integer.class, null, String.class));
}
class A1<T1, T2, T3> {}
class A2<T3, T2> extends A1<Integer, T2, T3> {}
class A3<T2> extends A2<String, T2> {}
class A4 extends A3<Boolean> {}
class B2<X, T3, Y, T2, Z> extends A1<Integer, T2, T3> {}
class B3<X, T2, Y, Z> extends B2<X, String, Y, T2, Z> {}
class B4<X> extends B3<X, Boolean, X, X> {}
class C2<T2, T3> extends A1<Integer, T2, T3> {}
class D2<T2> extends A1<Integer, T2, String> {}
class D3 extends D2 {}
class D4 extends D3 {}