Java Generics: доступ к универсальному типу во время выполнения - PullRequest
13 голосов
/ 03 марта 2012

Я ищу доступ к универсальному типу объявленного поля во время выполнения. Ранее у меня сложилось впечатление, что это было невозможно из-за стирания типа Java. Однако это не должно иметь место, потому что некоторые хорошо известные фреймворки используют универсальный тип посредством отражения во время выполнения.

Например, Guice будет реализовывать провайдера на основе предоставленного вами универсального типа:

public class Injectable{

    @Inject
    private Provider<SomeType> someTypeProvider;

}

Как получить доступ к универсальному атрибуту поля SomeType поля или любого такого типа / метода / и т. Д. Через API отражения?

Кроме того, было бы полезно также узнать, как получить доступ к этим атрибутам универсального типа через API процессора аннотаций Java 6.

Спасибо.

Изменить:

Спасибо всем за ваши замечательные советы. Я нашел способ сделать это с помощью ссылок Хейлема, в частности, на статью Пренкова Отражение Java: Generics .

Вот ответ, который я искал:

/**
 * @author John Ericksen
 */
public class TypeReflectionExample {

    public class SomeType{}

    public class Injectable{
        @Inject  private Provider<SomeType> someTypeProvider;
    }

    public static void main(String[] args){

        try {
            Field providerField = Injectable.class.getDeclaredField("someTypeProvider");

            Type genericFieldType = providerField.getGenericType();

            if(genericFieldType instanceof ParameterizedType){
                ParameterizedType aType = (ParameterizedType) genericFieldType;
                Type[] fieldArgTypes = aType.getActualTypeArguments();
                for(Type fieldArgType : fieldArgTypes){
                    Class fieldArgClass = (Class) fieldArgType;
                    System.out.println("fieldArgClass = " + fieldArgClass);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }   
}

Результат:

fieldArgClass = class test.TypeReflectionExample $ SomeType

То же самое можно сделать для методов, конструкторов, расширений / орудий суперкласса и т. Д.

Я награждаю хейлем, поскольку его пост привел меня к этому решению, даже если оно не дало прямого ответа на мой вопрос.

Ответы [ 4 ]

14 голосов
/ 03 марта 2012

Это правда, что дженерики обычно не известны во время выполнения в Java, потому что они реализованы с помощью Type Erasure.

Отражение дженериков?

Однако вы можете извлечь некоторую ценную информацию о объявленных типах (НЕ о типах объектов времени выполнения), как это представлено в статье Яна Роберстона Отражение обобщений и статья Пренкова Java Reflection: Generics .

Справочная информация по родовым типам и стиранию типов

Обобщения, введенные при сохранении обратной совместимости на исходном qnd двоичном уровне, отсюда и некоторые их ограничения, например:

  • невозможность иметь сокращенную форму без какого-либо индикатора поддержки дженериков (здесь так называемый оператор алмаза <>),
  • невозможность проверки универсальных типов во время выполнения, поскольку они должны были быть реализованы с помощью Erasure типа .

Дальнейшее чтение

4 голосов
/ 03 марта 2012

Ну, а почему бы вам не посмотреть, что делает ?Источник общедоступен.

Guice делает эти вещи на нескольких уровнях.Особо выделяются литералы типа .

. Ключевой момент здесь заключается в том, что в то время как типы скомпилированы с использованием стирания типов (поэтому существует только один класс для каждого типа), все еще существует несколько Type объектов, которые знают используемые обобщения.Тем не менее, код оптимизируется независимо от этого (как он был скомпилирован для класса, а не для типа).

Посмотрите на API Java ParameterizedType.

Таким образом, несмотря на то, что Generics Java реализован "стиранием типа" на уровне class , это не полностью соответствует уровню Type.К сожалению, обработка типов намного сложнее, чем классов.Кроме того, это также означает, что Java не может оптимизировать обобщенные элементы так же, как это делает C ++, в частности, для примитивных типов.ArrayList<Integer> по конструкции будет ArrayList<?>, содержащим объекты, и не будет поддерживаться собственным массивом int[], когда это возможно.

Обратите внимание, что это, однако, довольно близко к отслеживанию этих вещей самостоятельно,Скажем, очень наивно (это не будет работать с вложенными обобщениями), вы можете просто расширить ArrayList<T> с помощью класса, имеющего поле Class<T> contentClass, тогда вы сможете найти эту информацию во время выполнения.(Однако здесь TypeLiteral может быть лучшим выбором вместо класса!) Кроме того, JRE фактически не будет гарантировать, что список остается согласованным.Точно так же, как вы могли бы преобразовать ArrayList<Integer> в нетипизированный ArrayList и добавить объект String.

2 голосов
/ 03 марта 2012

Я использовал это год или около того назад.Это может помочь вам

Type typeOfSrc = type(YourClass.class, clazz);

// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
    return new ParameterizedType()
    {
        public Type getRawType(){ return raw; }

        public Type[] getActualTypeArguments(){ return args; }

        public Type getOwnerType(){ return null; }
    };
}

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

0 голосов
/ 04 марта 2012

Я полагаю, что guice использует TypeLiteral для инкапсуляции общей информации в отдельный объект.

...