Получить тип общего параметра в Java с отражением

Можно ли получить тип общего параметра?

Пример:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

Ответ 1

Одна конструкция, однажды наткнувшись, выглядела как

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

Итак, кажется, что вокруг есть какая-то магия отражения, которую я, к сожалению, не совсем понимаю... Извините.

Ответ 2

Я хочу попытаться сломать ответ от @DerMike, чтобы объяснить:

Во-первых, стирание типа не означает, что JDK исключает информацию о типе во время выполнения. Это метод, позволяющий совместную проверку типа времени компиляции и совместимость времени выполнения на одном языке. Как следует из этого блока кода, JDK сохраняет стертую информацию о типе - он просто не связан с проверенными нажатиями и т.д.

Во-вторых, это предоставляет общую информацию типа обобщенному классу ровно на один уровень вверх по иерархии от проверяемого конкретного типа - т.е. абстрактный родительский класс с параметрами общего типа может найти конкретные типы, соответствующие его параметрам типа, для конкретной реализации самого себя, которая непосредственно наследует его. Если этот класс был не абстрактным и инстанцированным, или конкретная реализация была двумя уровнями вниз, это не сработало бы (хотя немного jimmying могло бы сделать его применимым к любому заранее определенному количеству уровней выше одного или до самого низкого класса с параметрами X общего типа и т.д.).

В любом случае, к объяснению. Здесь код снова, разделенный на строки для облегчения ссылки:

1# Class genericParameter0OfThisClass = 
2#     (Class)
3#         ((ParameterizedType)
4#             getClass()
5#                .getGenericSuperclass())
6#                    .getActualTypeArguments()[0];

Пусть мы будем абстрактным классом с общими типами, которые содержат этот код. Читая это примерно наизнанку:

  •  
  • Строка 4 получает текущий конкретный класс "Экземпляр класса". Это идентифицирует наш непосредственный тип потомства.  
  • Строка 5 получает этот тип супертипа как Тип; это мы. Поскольку мы являемся параметрическим типом, мы можем смело относиться к ParameterizedType (строка 3). Ключ состоит в том, что когда Java определяет этот объект типа, он использует информацию о типе, присутствующую в дочернем элементе, для связывания информации о типе с нашими параметрами типа в новом экземпляре ParameterizedType. Теперь мы можем получить доступ к конкретным типам для наших дженериков.  
  • Строка 6 получает массив типов, отображаемых в наши обобщения, для того, чтобы они были объявлены в коде класса. В этом примере мы вытаскиваем первый параметр. Это возвращается как Тип.  
  • Строка 2 возвращает окончательный тип, возвращаемый классу. Это безопасно, потому что мы знаем, какие типы могут иметь параметры общего типа, и могут подтвердить, что все они будут классами (я не уверен, как в Java можно было бы получить общий параметр, который не имеет экземпляра класса связанные с ним, на самом деле).

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

Будет сложно, если ваши конкретные дети будут произвольным числом прыжков, или если вы конкретны, а не окончательны, и особенно сложно, если вы ожидаете, что любой из ваших (изменчиво глубоких) детей будет иметь свои собственные дженерики. Но вы можете обычно разрабатывать вокруг этих соображений, так что это дает вам большую часть пути.

Надеюсь, это помогло кому-то! Я признаю, что этот пост древний. Я, вероятно, отрежусь от этого объяснения и оставлю его для других вопросов.

Ответ 3

На самом деле я получил это, чтобы работать. Рассмотрим следующий фрагмент:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
     if( genericParameterTypes[i] instanceof ParameterizedType ) {
                Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

     }
 }

Я использую jdk 1.6

Ответ 4

На самом деле существует решение, применяя трюк анонимного класса анонимный класс и идеи Токены супер-типа:

public final class Voodoo {
    public static void chill(final List<?> aListWithSomeType) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) aListWithSomeType
            .getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]);
    }
    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>() {});
    }
}
class SpiderMan {
}

Трюк заключается в создании анонимного класса, new ArrayList<SpiderMan>() {} вместо исходного (простого) new ArrayList<SpiderMan>(). Использование анонимного класса (если возможно) гарантирует, что компилятор сохранит информацию о аргументе типа SpiderMan, заданном параметру типа List<?>. Voilà!

Ответ 5

Нет, это невозможно. Из-за проблем с пониженной совместимостью, Java-генераторы основаны на тире стирания, т.е. во время выполнения все, что у вас есть, это не общий List объект. Существует некоторая информация о параметрах типа во время выполнения, но она находится в определениях классов (т.е. Вы можете задать "

Ответ 6

Как указано @bertolami, нам не представляется возможным тип переменной и получить его будущее значение (содержимое переменной typeOfList).

Тем не менее вы можете передать класс как параметр на нем следующим образом:

public final class voodoo {
    public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = clazz;
    }

    public static void main(String... args) {
        chill(new List<SpiderMan>(), Spiderman.class );
    }
}

Это более или менее то, что делает Google, когда вам нужно передать переменную класса в конструктор ActivityInstrumentationTestCase2.

Ответ 7

Из-за стирания типа единственным способом узнать тип списка было бы передать тип в качестве параметра методу:

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}

Ответ 8

Приложение к ответу @DerMike для получения общего параметра параметризованного интерфейса (с использованием метода #getGenericInterfaces() внутри метода по умолчанию Java-8 во избежание дублирования):

import java.lang.reflect.ParameterizedType; 

public class ParametrizedStuff {

@SuppressWarnings("unchecked")
interface Awesomable<T> {
    default Class<T> parameterizedType() {
        return (Class<T>) ((ParameterizedType)
        this.getClass().getGenericInterfaces()[0])
            .getActualTypeArguments()[0];
    }
}

static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};

public static void main(String[] args) {
    System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
    // --> Type is: ParameterizedStuff$Beer
}

Ответ 9

Нет, это невозможно.

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

См. Знать тип обобщенного в Java для примера.

Ответ 10

Это невозможно, потому что generics в Java рассматриваются только во время компиляции. Таким образом, генераторы Java - это всего лишь предварительный процессор. Однако вы можете получить фактический класс членов списка.

Ответ 11

Вы не можете получить общий параметр из переменной. Но вы можете из описания метода или поля:

Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();

Ответ 12

Использование:

Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();

Ответ 13

Просто для меня чтение этого фрагмента кода было трудным, я просто разделил его на две читаемые строки:

// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

Я хотел создать экземпляр параметра Type без каких-либо параметров для моего метода:

publc T getNewTypeInstance(){
    ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
    Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

    // for me i wanted to get the type to create an instance
    // from the no-args default constructor
    T t = null;
    try{
        t = c.newInstance();
    }catch(Exception e){
        // no default constructor available
    }
    return t;
}

Ответ 14

Вы можете получить тип общего параметра с отражением, как в этом примере, который я нашел здесь:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Home<E> {
    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass(){
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>{}
    private static class StringBuilderHome extends Home<StringBuilder>{}
    private static class StringBufferHome extends Home<StringBuffer>{}   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }
}

Ответ 15

Я закодировал это для методов, которые ожидают принятия или возврата Iterable<?...>. Вот код:

/**
 * Assuming the given method returns or takes an Iterable<T>, this determines the type T.
 * T may or may not extend WindupVertexFrame.
 */
private static Class typeOfIterable(Method method, boolean setter)
{
    Type type;
    if (setter) {
        Type[] types = method.getGenericParameterTypes();
        // The first parameter to the method expected to be Iterable<...> .
        if (types.length == 0)
            throw new IllegalArgumentException("Given method has 0 params: " + method);
        type = types[0];
    }
    else {
        type = method.getGenericReturnType();
    }

    // Now get the parametrized type of the generic.
    if (!(type instanceof ParameterizedType))
        throw new IllegalArgumentException("Given method 1st param type is not parametrized generic: " + method);
    ParameterizedType pType = (ParameterizedType) type;
    final Type[] actualArgs = pType.getActualTypeArguments();
    if (actualArgs.length == 0)
        throw new IllegalArgumentException("Given method 1st param type is not parametrized generic: " + method);

    Type t = actualArgs[0];
    if (t instanceof Class)
        return (Class<?>) t;

    if (t instanceof TypeVariable){
        TypeVariable tv =  (TypeVariable) actualArgs[0];
        AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
        GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
        return (Class) tv.getAnnotatedBounds()[0].getType();
    }

    throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}