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