Получение аргументов типа параметризованного класса - PullRequest
7 голосов
/ 15 июня 2011

У меня есть класс 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 и получить аргументы, но я не смог найти, как.

Есть идеи?

Заранее спасибо.

Ответы [ 2 ]

3 голосов
/ 16 июня 2011

Я создал метод, который будет возвращать вам массив типов, которые параметризовали общий тип, где «ноль» указывает на параметры, которые все еще существуют.Я проверил это в соответствии с вашей иерархией 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 {}
3 голосов
/ 16 июня 2011

Это хороший подход - это единственный способ фактически получить информацию общего типа, скомпилированную в байт-коде Java. Все остальное с точки зрения обобщений в Java - это просто стирание типов. Смотрите ниже то, что кажется рабочим решением. Существует четыре сценария:

  1. Довольно сложный уровень наследования
  2. Ваш сценарий наследования
  3. Сам класс (clazz будет null)
  4. Подкласс, который не предоставляет фактические значения (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>();
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...