Получить тип универсального параметра в 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>());
}
}
1480   16  

16 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

есть решение на самом деле, применяя "анонимный класс" фишка и идеи от Супер Маркеры Типа:

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>(). Использование класса anoymous (если это возможно) гарантирует, что компилятор сохранит информацию о аргументе типа SpiderMan для параметра типа List<?>. Вуаля !

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

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

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) {

    }

}

как указал @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.

приложение к ответу @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
}

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

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

посмотреть зная тип generic в Java для примера, что.

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

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

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());
    }
}

я закодировал это для методов, которые ожидают принять или вернуть 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's 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's 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());
}

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

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

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

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

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

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;
}

быстрый ответ the вопрос нет, вы не можете, из-за стирания общего типа Java.

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

new ArrayList<SpideMan>(){}

тогда в этом случае универсальный тип сохраняется в универсальном суперклассе нового анонимного класса выше.

не то, что я рекомендую делать это со списками, но это реализация слушателя:

new Listener<Type>() { public void doSomething(Type t){...}}

и с момента экстраполяции общие типы суперклассов и суперинтерфейсов меняются между JVMs, общее решение не так прямолинейно, как могут предложить некоторые ответы.

вот теперь я это сделал.

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

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

Comments

    Ничего не найдено.