获取参数化类的类型参数

时间:2011-06-15 19:44:39

标签: java reflection

我有一个类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也是一个类,而不是变量。

问题是当我尝试实例化BazBaz -> Bar<Class2> -> Foo<T, Class2>)时它失败了。我得到一个ArrayIndexOutOfBoundsException,因为getActualTypeArguments()返回一个只包含类Class2(大小为1)的数组,而我正在尝试获取数组的第二个元素。那是因为我得到了Bar<Class2>的论据,而不是Foo的论据。

我想要的是以某种方式修改Foo构造函数我可以在参数U中获取类,如果我实例化的类是否是直接子类则无关紧要。我想我应该可以进入类的层次结构,直到达到类Foo,将其转换为ParameterizedType并获取参数,但我找不到。

有什么想法吗?

提前致谢。

2 个答案:

答案 0 :(得分:3)

这是一个很好的方法 - 它是实际获取在Java字节码中编译的泛型类型信息的唯一方法。 Java中泛型的其他东西只是类型擦除。见下文似乎是一个有效的解决方案。有四种情况:

  1. 相当复杂的继承级别
  2. 您的继承方案
  3. 课程本身(clazz将为null
  4. 不提供实际值(clazz = null
  5. 的子类

    这是代码(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 {}