Доступ к параметризованной информации о типе во время выполнения
Возможный дубликат:
Почему не все данные типа удаляются в Java во время выполнения?
Генераторы Java реализуются с помощью стирания типа, поэтому я думал, что во время выполнения невозможно получить какую-либо информацию о параметризованном типе. Тем не менее, я нашел следующий класс в библиотеке Джексона.
(Я немного упростил класс ради этого примера)
public abstract class TypeReference<T> {
final Type _type;
protected TypeReference() {
Type superClass = getClass().getGenericSuperclass();
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
}
public Type getType() { return _type; }
}
Класс предоставляет доступ к параметризованному типу, как показывает следующий тест:
void testTypeReference() {
// notice that we're instantiating an anonymous subclass of TypeReference
TypeReference<CurrencyDto> tr = new TypeReference<CurrencyDto>() {};
assert tr.getType() == CurrencyDto.class
}
Этот класс показывает, что фактические параметры типа могут быть получены во время выполнения (с использованием отражения), как это согласуется с понятием, что Java Generics реализованы с помощью стирания типа?
Ответы
Ответ 1
Информация о аргументе конкретного типа хранится в файлах классов, когда она известна во время компиляции. Например, если у вас есть класс с методом, который возвращает List<String>
(not List<T>
!), Эта информация будет доступна во время выполнения. Аналогично, если у вас есть класс:
public class Foo extends Bar<String> {
}
аргумент типа String
во время компиляции жестко привязан к Foo
. Класс TypeReference
(и все подобные конструкции, такие как TypeLiteral
в Guice и TypeToken
в Gson) используют этот факт, требуя от вас сделать анонимный подкласс этого кода. Когда вы это сделаете, фактический файл класса будет сгенерирован с этой информацией, как и с приведенным выше примером Foo
.
Для получения дополнительной информации см. сообщение в блоге Neal Gafter здесь.
Тип erasure больше относится к тому факту, что вы не можете получить информацию о фактических аргументах типа экземпляру родового типа во время выполнения (обратите внимание, что Foo
, подкласс Bar<String>
, не имеет переменных типа и не является общим). Например, если вы создаете экземпляр типового типа ArrayList<E>
:
List<String> foo = new ArrayList<String>();
информация о типе не сохраняется в этом объекте. Поэтому, когда вы передаете его другому методу:
public <T> T foo(List<T> list)
нет способа узнать, что такое T
.
Ответ 2
Это действительно очень просто: вы не можете получить общую информацию из значения INSTANCES, но вы можете получить ее из TYPES (classes) с некоторыми ограничениями. В частности, есть 3 места, где доступна информация о типовом типе (подробнее см. http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html); по описанию суперкласса/интерфейса (параметризация супер-типа), декларации полей и/или по методу (аргумент, тип возвращаемого типа).
В случае TypeReference происходит то, что вы создаете анонимный тип с указанным супер-типом; и эта информация будет доступна после того, как будет передан анонимный тип (класс).
Ответ 3
Я не вижу здесь никакого реального противоречия.
TypeReference будет считаться сырым типом TypeReference во время выполнения для устаревшего кода, но у него все еще есть возможность предоставить информацию о SomeType. Какой тип стирания означает, что вы не можете использовать T TypeReference во время выполнения, как приведенный здесь. Но вы все еще можете узнать, по какому Т заменяется.