Доступ к параметризованной информации о типе во время выполнения

Возможный дубликат:
Почему не все данные типа удаляются в 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 во время выполнения, как приведенный здесь. Но вы все еще можете узнать, по какому Т заменяется.