Вопрос по generics, types, reflection, java – Отражение для класса универсального параметра в Java?

8

Представьте себе следующий сценарий:

class MyClass extends OtherClass<String>{

   String myName;
   //Whatever

}

class OtherClass<T> {

   T myfield;

}

И я анализирую MyClass, используя отражение специально(MyClass.class).getDeclaredFields()в этом случае я получу следующие поля (и типы, используя getType () поля):

myName --> String
myField --> T

Я хочу получить фактический тип для T, который известен во время выполнения из-за явного & quot; String & quot; в расширенных обозначениях, как я могу получить негенетический тип myField?

РЕДАКТИРОВАТЬ РЕШЕНО:

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

Ответ не в том, что вы не можете & t; Вы можете легко создать карту из параметров типа в явные аргументы типа. Jeffrey

Ваш Ответ

6   ответов
0

Это классический пример того, почему рефлексия не очень хорошая идея.

What you can get from a program by reflection are only those facts that the compiler people for the language chose to make available.

И они, как правило, не могут позволить себе сделать все доступным; они вроде как должны держать исходный текст программы.

Таким образом, все остальные факты о вашем коде недоступны для рефлексирующего.

Лекарство от этого - шагoutside язык и использовать инструмент, которыйcan предоставить любую произвольную информацию о коде. Такие инструменты называютсяСистемы трансформации программ (PTS).

PTS анализирует исходный код и создает AST, который представляет его. Хороший PTW создаст AST, который содержит практически все о коде (операторы, операнды, знаки пунктуации, комментарии), чтобы его можно было проверить. Обычно PTS записывает положение строки / столбца языковых токенов, так что даже информация о расположении доступна; Extreme PTS будет записывать пробелы в промежутках между токенами или, по крайней мере, знать, как читать исходный текстовый файл, когда это необходимо, если его спросят об этом. Этот AST по существу эквивалентен полному тексту, который, как я сказал, будет необходим, но в более удобной форме для обработки.

(У ПТС есть еще одно очень приятное свойство: они могутmodify AST и восстановить код для модифицированной программы. Но это выше и вне рефлексии, поэтому я не буду комментировать дальше этот аспект).

0

Общие типыnot известно во время выполнения. Только компилятор знает о них, проверяет, правильно ли набрана ваша программа, а затем удаляет их.

В вашем конкретном случае звонитMyClass.class.getGenericSuperclass() может дать вам необходимую информацию, потому что по какой-то странной причине конкретные типы, используемые при наследовании, хранятся в дескрипторе класса.

Это не "какая-то странная причина".Declarations полей, методов, суперклассов, включающих классов и т. д. всегда сохраняются, потому что другие классы должны использовать их при компиляции. Это отдельная проблема от типов объектов времени выполнения, где нет обобщений.
20

Это может быть достигнуто с отражением только потому, что вы явно использовалиStringв противном случае эта информация была бы потеряна из-за стирания типа.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>
Тщательно с кастингом. В общем, это может быть Class или TypeVariable. В этом случае вам нужно проверить границы.
Это отличный ответ, однако, как я могу сделать это вообще с объектом Field? Я не хочу явно запрашивать суперкласс, потому что я просто пытаюсь получить все объявленные поля моего подкласса, одно из которых является универсальным. Я просто хочу что-то похожее на getDeclaredFields (), но умнее с дженериками. Имеет ли это смысл? Sam Stern
Похоже, вы выиграли конкурс на полный ответ :)
@hatboysam Так или иначе, вы должны попросить супер класс. призваниеField.getGenericParameter будет только когда-либо возвращать параметр типа (в этом случаеT), а не явный аргумент типа (String). Вы можете взять этот параметр типа и найти явный аргумент типа, создав карту изMyClass.class.getSuperclass().getTypeArguments() вMyClass.class.getGenericSuperclass().getActualTypeArguments().
3

Я нашел хорошее объяснениеВот:

When runtime inspecting a parameterizable type itself, like java.util.List, there is no way of knowing what type is has been parameterized to. This makes sense since the type can be parameterized to all kinds of types in the same application. But, when you inspect the method or field that declares the use of a parameterized type, you can see at runtime what type the paramerizable type was parameterized to.

Короче:

Вы не можете увидеть сам тип, какой тип он параметризован во время выполнения, но вы можете увидеть его в полях и методах, где он используется и параметризован.

В коде:

Вы не можете видетьT Вот:

class MyClass<T> { T myField; }

Вы можете увидеть & quot;T& Quot; Вот:

class FooClass {
    MyClass<? extends Serializable> fooField;
}

Здесь вы сможете сказать тип и параметры типаfooField. See getGeneric*() методыClass а такжеMethod.

Кстати, я часто вижу это (сокращенно):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0];

Это не правильно, потому чтоgetActualTypeArguments() может и часто вернетсяTypeVariable вместо класса - это когда универсальный<? extends SomeClass> вместо просто<SomeClass>, Это может пойти глубже, представьте:

class FooClass {
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField;
}

Таким образом, вы получите деревоTypes. Но это немного не по теме. Наслаждаться :)

0

большая работа вокругВот, это называется "Gafters Gadget" шаблон. он используется библиотеками Джексона и Google, такими как гуава.

/** * References a generic type. * * @author [email protected] (Bob Lee) */


public abstract class TypeReference {

private final Type type; private volatile Constructor<?> constructor; protected TypeReference() { Type superclass = getClass().getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; } /** * Instantiates a new instance of {@code T} using the default, no-arg * constructor. */ @SuppressWarnings("unchecked") public T newInstance() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (constructor == null) { Class<?> rawType = type instanceof Class<?> ? (Class<?>) type : (Class<?>) ((ParameterizedType) type).getRawType(); constructor = rawType.getConstructor(); } return (T) constructor.newInstance(); } /** * Gets the referenced type. */ public Type getType() { return this.type; } public static void main(String[] args) throws Exception { List<String> l1 = new TypeReference<ArrayList<String>>() {}.newInstance(); List l2 = new TypeReference<ArrayList>() {}.newInstance(); }

}

-5

Нет прямого способа получить фактический тип из-заТип Erasure, Однако вы можете использовать следующий способ:

в вашемOtherClass<T>напишите следующий абстрактный метод:

protected abstract class<T> getClazz();

Затем вMyClass, вы реализуете метод:

@Override
protected Class<String> getClazz(){
    return String.class;
}

тогда вы можете позвонитьgetClazz() чтобы получить класс.

@ Джеффри, используя свой метод, вы можете получить тип вMyClass, но вы не можете получить это вOtherClass
ОП дал конкретный параметр типа для своего универсального класса, эту информациюis хранится во время выполнения и может быть получен.
+1 за ссылку на тип Erasure
-1 - Это решение не нужно, как и другие отмеченные в этом посте. Кроме того, вашgetClass() будет иметь конфликт имени с последним методомObject#getClass()

Похожие вопросы