среда, 2 марта 2011 г.

Java. Как получить информацию о параметре универсального типа в runtime

При компиляции универсальных типов (в частности, списков(List)) в Java, как известно, информация о параметре типа (<T>) удаляется, и в рантайме получить эту информацию нельзя. Но, как говорится, если нельзя но очень хочется... :)
Сразу оговорюсь, с полями и переменными, действительно сделать ничего нельзя и определить параметр типа, который содержится внутри не получится (в смысле, что я на данный момент такого способа не знаю). А вот с параметрами метода очень даже можно !

Итак...

Задача 1

Дано :
Объект класса с методом void setMyList(List<String> param){}

Требуется :
во время выполнения, определить тип, содержащийся в списке List параметра param

Решение :

public static Class getClassName(Method method)
  Class clazz;
  Type[] types = method.getGenericParameterTypes();
  for (Type type : types) {
                // генерик
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;
                    Class = (Class) pt.getActualTypeArguments()[0];
                }
                // обычный тип (не генерик)
                if (type instanceof Class) {
                    clazz = (Class) type;
                }
            }
  return clazz;
}


Как получить Method класса, я здесь останавливаться не буду, это можно почитать в статьях про reflection API.


Усложним ситуацию, и предположим, что мы инстанцировали объект, с помощью springframework, и по каким-то причинам
этот объект инстанцирован не явно, а через оболочку-прокси, с использованием CGLIB. Примером может служить создание объектов в контексте безопасности spring-security. Вобщем суть в том, что вместо объекта нашего собственного класса, мы работаем с объектом класса-прокси, и в описании метода (того самого Method) у него уже нет информации о типе генерика. Но, вода дырочку найдёт...

Задача 2

Дано :
Объект класса, инстанцированного через spring, с включённым контекстом безопасности, с методом void setMyList(List<String> param){}

Требуется :
во время выполнения, определить тип, содержащийся в списке List параметра param

Решение :
public static Class getClassNameCGILIB(Method method) {
  Class clazz;
  if (!AopUtils.isCglibProxyClass(method.getDeclaringClass())) {
     Class baseClass = method.getDeclaringClass().getSuperclass(); // исходный класс, для которого сделана заглушка
     try {
          Method baseMethod = baseClass.getMethod(method.getName(), method.getParameterTypes());
         clazz = getClassName(baseMethod);
     } catch (NoSuchMethodException e) {
         e.printStackTrace();  //TODO сделать обработку ошибок
     }   
  }
}


P.S.: Описанные методы точно работают на Sun JDK 1.6.

Комментариев нет:

Отправить комментарий