Вопрос-ответ

Get type of a generic parameter in Java with reflection

Получить тип универсального параметра в Java с отражением

Возможно ли получить тип универсального параметра?

Пример:

public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}

public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
Переведено автоматически
Ответ 1

Одна конструкция, на которую я однажды наткнулся, выглядела следующим образом

Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];

Итак, похоже, что вокруг есть какая-то магия отражения, которую я, к сожалению, не до конца понимаю... Извините.

Ответ 2

Я хочу попытаться разобрать ответ от @DerMike, чтобы объяснить:

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

Во-вторых, это предоставляет информацию об универсальном типе универсальному классу ровно на один уровень выше по иерархии от проверяемого конкретного типа - т. Е. Абстрактный родительский класс с параметрами универсального типа может найти конкретные типы, соответствующие его параметрам типа, для конкретной реализации самого себя, которая непосредственно наследуется от него. Если бы этот класс был неабстрактным и создавался в виде экземпляра, или конкретная реализация была бы на два уровня ниже, это бы не сработало (хотя небольшая доработка могла бы заставить его применяться к любому заранее определенному количеству уровней, превышающему один, или вплоть до самого низкого класса с параметрами X универсального типа, и так далее).

В любом случае, перейдем к объяснению. Вот снова код, разделенный на строки для удобства использования:

1# Class genericParameter0OfThisClass = 
2# (Class)
3# ((ParameterizedType)
4# getClass()
5# .getGenericSuperclass())
6# .getActualTypeArguments()[0];

Пусть 'us' будет абстрактным классом с универсальными типами, который содержит этот код. Читаем это примерно наизнанку.:


  • Строка 4 возвращает экземпляр класса текущего конкретного класса. Это идентифицирует конкретный тип нашего непосредственного потомка.
  • Строка 5 получает супертип этого класса в качестве типа; это us . Поскольку мы являемся параметрическим типом, мы можем безопасно привести себя к ParameterizedType (строка 3). Ключ в том, что когда Java определяет этот объект типа, она использует информацию о типе, присутствующую в дочернем объекте, чтобы связать информацию о типе с нашими параметрами типа в новом экземпляре ParameterizedType . Итак, теперь мы можем получить доступ к конкретным типам для наших дженериков.
  • Строка 6 возвращает массив типов, отображенных в наши общие параметры, в порядке, объявленном в коде класса. В этом примере мы извлекаем первый параметр. Он возвращается как тип.
  • Строка 2 приводит конечный тип, возвращаемый классу. Это безопасно, потому что мы знаем, какие типы могут принимать наши параметры универсального типа, и можем подтвердить, что все они будут классами (я не уверен, как в Java можно получить универсальный параметр, с которым на самом деле не связан экземпляр класса).

...и это в значительной степени все. Итак, мы переносим информацию о типе из нашей собственной конкретной реализации обратно в себя и используем ее для доступа к дескриптору класса. мы могли бы удвоить getGenericSuperclass() и перейти на два уровня, или исключить getGenericSuperclass() и получить значения для себя как конкретный тип (предостережение: я не тестировал эти сценарии, они еще не придуманы для меня).

Это становится сложным, если ваши конкретные дочерние элементы находятся на расстоянии произвольного количества переходов, или если вы конкретный, а не окончательный вариант, и особенно сложно, если вы ожидаете, что у любого из ваших (с переменной глубиной) дочерних элементов будут свои собственные обобщения. Но обычно вы можете проектировать с учетом этих соображений, так что это поможет вам в большей степени.

Надеюсь, это кому-то помогло! Я признаю, что этот пост древний. Я, вероятно, сокращу это объяснение и сохраню его для других вопросов.

Ответ 3

На самом деле я заставил это работать. Рассмотрим следующий фрагмент:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

}
}

Я использую jdk 1.6

Ответ 4

На самом деле есть решение, путем применения трюка с "анонимным классом" и идей из токенов супертипа:

public final class Voodoo {
public static void chill(final List<?> aListWithSomeType) {
// Here I'd like to get the Class-Object 'SpiderMan'
System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
System.out.println(((ParameterizedType) aListWithSomeType
.getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>() {});
}
}
class SpiderMan {
}

Хитрость заключается в создании анонимного класса, new ArrayList<SpiderMan>() {} вместо исходного (простого) new ArrayList<SpiderMan>(). Использование анонимного класса (если возможно) гарантирует, что компилятор сохранит информацию о аргументе типа, SpiderMan заданном параметру типа List<?>. Voilà !

java generics reflection