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

Возможный дубликат:
Почему не вся информация о типах стирается в 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 реализуется посредством стирания типа?


person Dónal    schedule 09.06.2011    source источник
comment
Я не думаю, что это дубликат другого вопроса. Этот вопрос спрашивает, почему компилятор хранит эту информацию, тогда как это требует объяснения того, как это согласуется с понятием стирания типа.   -  person ColinD    schedule 09.06.2011


Ответы (3)


Информация об аргументах типа Concrete хранится в файлах классов, если она известна во время компиляции. Например, если у вас есть класс с методом, который возвращает List<String> (не List<T>!), эта информация будет доступна во время выполнения. Точно так же, если у вас есть класс:

public class Foo extends Bar<String> {
}

аргумент типа String жестко закодирован в Foo во время компиляции. Класс TypeReference (и все подобные конструкции, такие как TypeLiteral в Guice и TypeToken в Gson) использует этот факт, требуя от вас создания анонимного подкласса в вашем коде. Когда вы сделаете это, будет сгенерирован фактический файл класса с этой информацией, как и в примере Foo выше.

Для получения дополнительной информации см. сообщение в блоге Нила Гафтера здесь.

Стирание типа больше относится к тому факту, что вы не можете получить информацию о фактических аргументах типа для экземпляра универсального типа во время выполнения (обратите внимание, что Foo, подкласс Bar<String>, не имеет переменных типа и не является универсальным). Например, если вы создаете экземпляр универсального типа ArrayList<E>:

List<String> foo = new ArrayList<String>();

в этом объекте не хранится информация о типе. Итак, когда вы передаете его другому методу:

public <T> T foo(List<T> list)

нет никакого способа узнать, что такое T.

person ColinD    schedule 09.06.2011
comment
Не правильнее ли было бы сказать, что невозможно узнать, что такое Т, если список пуст? Я чувствую себя немного толстоватым, но я не могу примириться с тем, что информация о типе доступна во время выполнения, если список не пуст, выполнив foo.get(0).getClass(). Хотя я понимаю, что мы не обязательно получаем T, поскольку объекты в списке могут быть подклассами T. - person Marcus Junius Brutus; 02.06.2013
comment
@Marcus Junius Brutus: T не обязательно является типом первого элемента. Например, первый элемент может быть Double, а TNumber или Object. Кроме того, имейте в виду, что не каждый универсальный класс является List или каким-либо другим контейнером, где вы можете получить экземпляр T, вызвав метод. Comparator, например, имеет только метод, которому вы передаете экземпляры T. - person ColinD; 03.06.2013

Это действительно очень просто: вы не можете получить общую информацию из значений INSTANCES, но вы можете получить ее из TYPES (классов) с некоторыми ограничениями. В частности, есть 3 места, где доступна информация об общем типе (см. http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html для подробностей); в объявлении суперкласса/интерфейса (параметризация супертипа), в объявлениях полей и в объявлениях методов (аргумент, возвращаемый тип).

В случае TypeReference происходит то, что вы создаете анонимный тип с указанным супертипом; и эта информация будет доступна после передачи этого анонимного типа (класса).

person StaxMan    schedule 09.06.2011
comment
на самом деле очень просто - тут я бы с вами не согласился. - person Simon Nickerson; 09.06.2011
comment
Хе. Что ж, идея о том, что VALUE не имеет информации об универсальном типе, но есть у определений классов, проста. Не дженерики в целом, на что я бы не претендовал. :) Но FWIW, это (angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) может чтобы можно было понять, как это все работает. - person StaxMan; 09.06.2011
comment
@StaxMan, спасибо за ответ и интересные ссылки. - person Yann-Gaël Guéhéneuc; 14.04.2013

Я не вижу здесь реального противоречия.

TypeReference будет рассматриваться как необработанный тип TypeReference во время выполнения для устаревшего кода, но он по-прежнему может предоставлять информацию о SomeType. Стирание типа означает, что вы не можете использовать T TypeReference во время выполнения как пример здесь. Но вы все равно можете узнать, чем заменяется T.

person Snicolas    schedule 09.06.2011