import com.fasterxml.jackson.core.type.TypeReference; new TypeReference<T>(){}
Богдан Шульга
Ответы:
570
Короткий ответ заключается в том, что нет способа узнать тип времени выполнения параметров универсального типа в Java. Я предлагаю прочитать главу о стирании типов в Учебном руководстве по Java для получения более подробной информации.
Популярное решение этого заключается в передаче Classпараметра типа в конструктор универсального типа, например
classFoo<T>{finalClass<T> typeParameterClass;publicFoo(Class<T> typeParameterClass){this.typeParameterClass = typeParameterClass;}publicvoid bar(){// you can access the typeParameterClass here and do whatever you like}}
Этот ответ дает правильное решение, но неточно сказать, что нет способа найти универсальный тип во время выполнения. Оказывается, стирание типа намного сложнее, чем стирание. Мой ответ показывает, как получить классы универсального типа.
Бен Терли
3
@BenThurley Аккуратный трюк, но, насколько я вижу, он работает, только если есть универсальный супертип для него. В моем примере вы не можете получить тип T в Foo <T>.
Жолт Тёрёк
@webjockey Нет, ты не должен. Назначение typeParameterClassбез назначения по умолчанию в конструкторе прекрасно. Нет необходимости устанавливать его во второй раз.
Adowrath
Это первое решение, которое приходит на ум, но порой не вы создаете / инициируете объекты. Так что вы не сможете использовать конструктор. Например, при извлечении объектов JPA из базы данных.
Парамвир Сингх Карвал
234
Я искал способ сделать это сам, не добавляя дополнительную зависимость к пути к классам. После некоторого исследования я обнаружил , что это возможно , до тех пор , пока у вас есть общий супертип. Это было нормально для меня, так как я работал со слоем DAO с универсальным супертипом слоя. Если это соответствует вашему сценарию, то это ИМХО самый подходящий подход.
В большинстве случаев использования дженериков, с которыми я сталкивался, есть какой-то универсальный супертип, например, List<T>для ArrayList<T>или GenericDAO<T>для DAO<T>и т. Д.
Мой проект использовал Spring, что еще лучше, поскольку у Spring есть удобный метод для поиска типа. Это лучший подход для меня, так как выглядит лучше всего. Я думаю, если бы вы не использовали Spring, вы могли бы написать свой собственный служебный метод.
import org.springframework.core.GenericTypeResolver;publicabstractclassAbstractHibernateDao<T extendsDomainObject>implementsDataAccessObject<T>{@AutowiredprivateSessionFactory sessionFactory;privatefinalClass<T> genericType;privatefinalString RECORD_COUNT_HQL;privatefinalString FIND_ALL_HQL;@SuppressWarnings("unchecked")publicAbstractHibernateDao(){this.genericType =(Class<T>)GenericTypeResolver.resolveTypeArgument(getClass(),AbstractHibernateDao.class);this.RECORD_COUNT_HQL ="select count(*) from "+this.genericType.getName();this.FIND_ALL_HQL ="from "+this.genericType.getName()+" t ";}
просьба разъяснить смысл из resolveTypeArgument аргументов
gstackoverflow
getClass () - это метод java.lang.Object, который будет возвращать класс конкретного объекта во время выполнения, это объект, для которого вы хотите разрешить тип. AbstractHibernateDao.class - это просто имя базового класса или суперкласса иерархии классов универсальных типов. Оператор импорта включен, так что вы сможете легко найти документы и проверить себя. Это страница docs.spring.io/spring/docs/current/javadoc-api/org/…
Бен Терли
Что такое пружинное решение с версией 4.3.6 и выше. Не работает с пружиной 4.3.6.
@ AlikElzin-kilaka инициализируется в конструкторе с использованием класса Spring GenericTypeResolver.
Бен Терли
104
Однако есть небольшая лазейка: если вы определяете свой Fooкласс как абстрактный. Это будет означать, что вы должны создать свой класс как:
Foo<MyType> myFoo =newFoo<MyType>(){};
(Обратите внимание на двойные скобки в конце.)
Теперь вы можете получить тип Tво время выполнения:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType =((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Однако обратите внимание, что mySuperclassэто должен быть суперкласс определения класса, фактически определяющий конечный тип для T.
Это также не очень элегантно, но вы должны решить, предпочитаете ли вы new Foo<MyType>(){}или new Foo<MyType>(MyType.class);в вашем коде.
Например:
import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.ArrayDeque;import java.util.Deque;import java.util.NoSuchElementException;/**
* Captures and silently ignores stack exceptions upon popping.
*/publicabstractclassSilentStack<E>extendsArrayDeque<E>{public E pop(){try{returnsuper.pop();}catch(NoSuchElementException nsee ){return create();}}public E create(){try{Type sooper = getClass().getGenericSuperclass();Type t =((ParameterizedType)sooper).getActualTypeArguments()[0];return(E)(Class.forName( t.toString()).newInstance());}catch(Exception e ){returnnull;}}}
Затем:
publicclassMain{// Note the braces...privateDeque<String> stack =newSilentStack<String>(){};publicstaticvoid main(String args[]){// Returns a new instance of String.String s = stack.pop();System.out.printf("s = '%s'\n", s );}}
Это легко лучший ответ здесь! Кроме того, что бы это ни стоило, это стратегия, которую Google Guice использует для связывания классовTypeLiteral
ATG
14
Обратите внимание, что каждый раз, когда используется этот метод построения объекта, создается новый анонимный класс. Другими словами, два объекта aи bсозданный таким образом , будет как продлить тот же класс , но не имеют одинаковые классы экземпляра. a.getClass() != b.getClass()
Мартин Серрано
3
Есть один сценарий, в котором это не работает. Если Foo должен реализовать интерфейс, такой как Serializable, то анонимный класс не будет Serializable, если не создан экземпляр класса. Я попытался обойти его, создав сериализуемый фабричный класс, который создает анонимный класс, производный от Foo, но затем, по какой-то причине, getActualTypeArguments возвращает универсальный тип вместо фактического класса. Например: (новый FooFactory <MyType> ()). CreateFoo ()
Лиор Чага
38
Стандартный подход / обходной путь / решение заключается в добавлении classобъекта в конструктор (ы), например:
publicclassFoo<T>{privateClass<T> type;publicFoo(Class<T> type){this.type = type;}publicClass<T> getType(){return type;}public T newInstance(){return type.newInstance();}}
Но, кажется, не может @autowired в реальном использовании, каким-либо образом обойти?
Альфред Хуанг
@AlfredHuang Обходной путь должен был бы создать bean-компонент для класса, который делает это, а не полагается на автопроводку.
Калебж
20
Представьте, что у вас есть абстрактный суперкласс, который является общим:
publicabstractclassFoo<?extends T>{}
И затем у вас есть второй класс, который расширяет Foo универсальным Bar, расширяющим T:
publicclassSecondextendsFoo<Bar>{}
Вы можете получить класс Bar.classв классе Foo, выбрав Type(из ответа Берта Бруйнооха) и выведя его, используя Classэкземпляр:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType =((ParameterizedType)mySuperclass).getActualTypeArguments()[0];//Parse it as StringString className = tType.toString().split(" ")[1];Class clazz =Class.forName(className);
Следует заметить, что эта операция не идеальна, поэтому рекомендуется кэшировать вычисленное значение, чтобы избежать многократных вычислений. Одно из типичных применений - это общая реализация DAO.
toString().split(" ")[1]это была проблема, избегайте"class "
IgniteCoders
16
Вот рабочее решение:
@SuppressWarnings("unchecked")privateClass<T> getGenericTypeClass(){try{String className =((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();Class<?> clazz =Class.forName(className);return(Class<T>) clazz;}catch(Exception e){thrownewIllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");}}
ЗАМЕЧАНИЯ:
Может использоваться только как суперкласс
Должен быть расширен типизированным классом ( Child extends Generic<Integer>)
ИЛИ
Должен быть создан как анонимная реализация ( new Generic<Integer>() {};)
Лучшим маршрутом, чем класс, предложенный другими, является передача объекта, который может сделать то же, что вы сделали бы с классом, например, создать новый экземпляр.
interfaceFactory<T>{
T apply();}<T>voidList<T> make10(Factory<T> factory){List<T> result =newArrayList<T>();for(int a =0; a <10; a++)
result.add(factory.apply());return result;}classFooFactory<T>implementsFactory<Foo<T>>{publicFoo<T> apply(){returnnewFoo<T>();}}List<Foo<Integer>> foos = make10(newFooFactory<Integer>());
@ Рикки Кларксон: Я не понимаю, как этот завод должен возвращать параметризованные foos. Не могли бы вы объяснить, как вы получаете Foo <T> из этого? Мне кажется, это дает только непараметризованный Фу. Разве T в make10 не просто Foo здесь?
ib84
@ ib84 Я исправил код; Кажется, я упустил, что Foo был параметризован, когда я первоначально написал ответ.
Рикки Кларксон,
9
У меня была эта проблема в абстрактном родовом классе. В данном конкретном случае решение проще:
Да, это так, но я хотел бы оставить очень минимум, который необходимо сделать при расширении этого класса. Проверьте ответ
droidpl
5
У меня есть (некрасивое, но эффективное) решение этой проблемы, которое я использовал недавно:
import java.lang.reflect.TypeVariable;publicstatic<T>Class<T> getGenericClass(){
__<T> ins =new __<T>();TypeVariable<?>[] cls = ins.getClass().getTypeParameters();return(Class<T>)cls[0].getClass();}privatefinalclass __<T>// generic helper class which does only provide type information{private __(){}}
Я нашел общий и простой способ сделать это. В моем классе я создал метод, который возвращает универсальный тип в соответствии с его положением в определении класса. Давайте предположим, что определение класса выглядит так:
publicclassMyClass<A, B, C>{}
Теперь давайте создадим некоторые атрибуты для сохранения типов:
publicclassMyClass<A, B, C>{privateClass<A> aType;privateClass<B> bType;privateClass<C> cType;// Getters and setters (not necessary if you are going to use them internally)}
Затем вы можете создать универсальный метод, который возвращает тип на основе индекса универсального определения:
/**
* Returns a {@link Type} object to identify generic types
* @return type
*/privateType getGenericClassType(int index){// To make it use generics without supplying the class typeType type = getClass().getGenericSuperclass();while(!(type instanceofParameterizedType)){if(type instanceofParameterizedType){
type =((Class<?>)((ParameterizedType) type).getRawType()).getGenericSuperclass();}else{
type =((Class<?>) type).getGenericSuperclass();}}return((ParameterizedType) type).getActualTypeArguments()[index];}
Наконец, в конструкторе просто вызовите метод и отправьте индекс для каждого типа. Полный код должен выглядеть так:
publicclassMyClass<A, B, C>{privateClass<A> aType;privateClass<B> bType;privateClass<C> cType;publicMyClass(){this.aType =(Class<A>) getGenericClassType(0);this.bType =(Class<B>) getGenericClassType(1);this.cType =(Class<C>) getGenericClassType(2);}/**
* Returns a {@link Type} object to identify generic types
* @return type
*/privateType getGenericClassType(int index){Type type = getClass().getGenericSuperclass();while(!(type instanceofParameterizedType)){if(type instanceofParameterizedType){
type =((Class<?>)((ParameterizedType) type).getRawType()).getGenericSuperclass();}else{
type =((Class<?>) type).getGenericSuperclass();}}return((ParameterizedType) type).getActualTypeArguments()[index];}}
Как объяснялось в других ответах, чтобы использовать этот ParameterizedTypeподход, вам нужно расширить класс, но это кажется дополнительной работой, чтобы создать целый новый класс, который расширяет его ...
Таким образом, делая класс абстрактным, он заставляет вас расширять его, удовлетворяя, таким образом, требованиям подкласса. (используя @Getter Ломбока).
Теперь, чтобы расширить его, не определяя новый класс. (Обратите внимание на {} в конце ... расширенный, но не перезаписывайте ничего - если не хотите).
public<T> T yourMethodSignature(Class<T> type){// get some object and check the type match the given typeObject result =...if(type.isAssignableFrom(result.getClass())){return(T)result;}else{// handle the error}}
Если вы расширяете или реализуете какой-либо класс / интерфейс, использующий обобщенные типы, вы можете получить универсальный тип родительского класса / интерфейса, не изменяя вообще никакого существующего класса / интерфейса.
Там может быть три возможности,
Случай 1
Когда ваш класс расширяет класс, который использует Generics
Это довольно просто. Если вам нужно из одного класса:
Class clazz =this.getClass();ParameterizedType parameterizedType =(ParameterizedType) clazz.getGenericSuperclass();try{Class typeClass =Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName());// You have the instance of type 'T' in typeClass variableSystem.out.println("Class instance name: "+ typeClass.getName());}catch(ClassNotFoundException e){System.out.println("ClassNotFound!! Something wrong! "+ e.getMessage());}
На самом деле, я полагаю, у вас есть поле в вашем классе типа T. Если нет поля типа T, какой смысл иметь универсальный тип? Таким образом, вы можете просто сделать экземпляр этого поля.
В моем случае у меня есть
Список <T> элементов;
в моем классе, и я проверяю, является ли тип класса "Местность"
if (items.get (0) instanceof Locality) ...
Конечно, это работает, только если общее количество возможных классов ограничено.
classGenericClass<T>(private val rawType:Class<*>){
constructor():this(`$Gson$Types`.getRawType(object :TypeToken<T>(){}.getType()))
fun getRawType():Class<T>{return rawType as Class<T>}}
Я хотел передать T.class методу, который использует Generics
Метод readFile читает файл .csv, указанный в fileName, с полным путем. Могут быть файлы csv с различным содержанием, поэтому мне нужно передать класс файла модели, чтобы я мог получить соответствующие объекты. Поскольку это чтение CSV-файла, я хотел сделать в общем. По тем или иным причинам ни одно из вышеперечисленных решений не помогло мне. Мне нужно использовать,
Class<? extends T> typeчтобы это работало. Я использую библиотеку opencsv для анализа файлов CSV.
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
Ответы:
Короткий ответ заключается в том, что нет способа узнать тип времени выполнения параметров универсального типа в Java. Я предлагаю прочитать главу о стирании типов в Учебном руководстве по Java для получения более подробной информации.
Популярное решение этого заключается в передаче
Class
параметра типа в конструктор универсального типа, напримеристочник
typeParameterClass
без назначения по умолчанию в конструкторе прекрасно. Нет необходимости устанавливать его во второй раз.Я искал способ сделать это сам, не добавляя дополнительную зависимость к пути к классам. После некоторого исследования я обнаружил , что это возможно , до тех пор , пока у вас есть общий супертип. Это было нормально для меня, так как я работал со слоем DAO с универсальным супертипом слоя. Если это соответствует вашему сценарию, то это ИМХО самый подходящий подход.
В большинстве случаев использования дженериков, с которыми я сталкивался, есть какой-то универсальный супертип, например,
List<T>
дляArrayList<T>
илиGenericDAO<T>
дляDAO<T>
и т. Д.Чистое решение Java
В статье Доступ к универсальным типам во время выполнения в Java объясняется, как вы можете сделать это с помощью чистой Java.
Весеннее решение
Мой проект использовал Spring, что еще лучше, поскольку у Spring есть удобный метод для поиска типа. Это лучший подход для меня, так как выглядит лучше всего. Я думаю, если бы вы не использовали Spring, вы могли бы написать свой собственный служебный метод.
источник
Однако есть небольшая лазейка: если вы определяете свой
Foo
класс как абстрактный. Это будет означать, что вы должны создать свой класс как:(Обратите внимание на двойные скобки в конце.)
Теперь вы можете получить тип
T
во время выполнения:Однако обратите внимание, что
mySuperclass
это должен быть суперкласс определения класса, фактически определяющий конечный тип дляT
.Это также не очень элегантно, но вы должны решить, предпочитаете ли вы
new Foo<MyType>(){}
илиnew Foo<MyType>(MyType.class);
в вашем коде.Например:
Затем:
источник
TypeLiteral
a
иb
созданный таким образом , будет как продлить тот же класс , но не имеют одинаковые классы экземпляра.a.getClass() != b.getClass()
Стандартный подход / обходной путь / решение заключается в добавлении
class
объекта в конструктор (ы), например:источник
Представьте, что у вас есть абстрактный суперкласс, который является общим:
И затем у вас есть второй класс, который расширяет Foo универсальным Bar, расширяющим T:
Вы можете получить класс
Bar.class
в классе Foo, выбравType
(из ответа Берта Бруйнооха) и выведя его, используяClass
экземпляр:Следует заметить, что эта операция не идеальна, поэтому рекомендуется кэшировать вычисленное значение, чтобы избежать многократных вычислений. Одно из типичных применений - это общая реализация DAO.
Окончательная реализация:
Возвращаемое значение - Bar.class при вызове из класса Foo в другой функции или из класса Bar.
источник
toString().split(" ")[1]
это была проблема, избегайте"class "
Вот рабочее решение:
ЗАМЕЧАНИЯ: Может использоваться только как суперкласс
Child extends Generic<Integer>
)ИЛИ
new Generic<Integer>() {};
)источник
Вы не можете сделать это из-за стирания типа. См. Также вопрос переполнения стека. Обобщения Java - стирание типов - когда и что происходит .
источник
Лучшим маршрутом, чем класс, предложенный другими, является передача объекта, который может сделать то же, что вы сделали бы с классом, например, создать новый экземпляр.
источник
У меня была эта проблема в абстрактном родовом классе. В данном конкретном случае решение проще:
и позже производный класс:
источник
У меня есть (некрасивое, но эффективное) решение этой проблемы, которое я использовал недавно:
источник
Возможно:
Вам нужны две функции из hibernate-generic-dao / blob / master / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java .
Дополнительные сведения см. В разделе « Отражение обобщений» .
источник
Я нашел общий и простой способ сделать это. В моем классе я создал метод, который возвращает универсальный тип в соответствии с его положением в определении класса. Давайте предположим, что определение класса выглядит так:
Теперь давайте создадим некоторые атрибуты для сохранения типов:
Затем вы можете создать универсальный метод, который возвращает тип на основе индекса универсального определения:
Наконец, в конструкторе просто вызовите метод и отправьте индекс для каждого типа. Полный код должен выглядеть так:
источник
Как объяснялось в других ответах, чтобы использовать этот
ParameterizedType
подход, вам нужно расширить класс, но это кажется дополнительной работой, чтобы создать целый новый класс, который расширяет его ...Таким образом, делая класс абстрактным, он заставляет вас расширять его, удовлетворяя, таким образом, требованиям подкласса. (используя @Getter Ломбока).
Теперь, чтобы расширить его, не определяя новый класс. (Обратите внимание на {} в конце ... расширенный, но не перезаписывайте ничего - если не хотите).
источник
Я предполагаю, что, поскольку у вас есть универсальный класс, у вас будет такая переменная:
(эта переменная должна принимать значение в конструкторе)
В этом случае вы можете просто создать следующий метод:
Надеюсь, поможет!
источник
источник
Если вы расширяете или реализуете какой-либо класс / интерфейс, использующий обобщенные типы, вы можете получить универсальный тип родительского класса / интерфейса, не изменяя вообще никакого существующего класса / интерфейса.
Там может быть три возможности,
Случай 1 Когда ваш класс расширяет класс, который использует Generics
Случай 2, когда ваш класс реализует интерфейс, который использует Generics
Случай 3 Когда ваш интерфейс расширяет интерфейс, который использует Generics
источник
Это довольно просто. Если вам нужно из одного класса:
источник
На самом деле, я полагаю, у вас есть поле в вашем классе типа T. Если нет поля типа T, какой смысл иметь универсальный тип? Таким образом, вы можете просто сделать экземпляр этого поля.
В моем случае у меня есть
в моем классе, и я проверяю, является ли тип класса "Местность"Конечно, это работает, только если общее количество возможных классов ограничено.
источник
Этот вопрос старый, но сейчас лучше всего использовать Google
Gson
.Пример получить на заказ
viewModel
.Класс родового типа
источник
Я хотел передать T.class методу, который использует Generics
Метод readFile читает файл .csv, указанный в fileName, с полным путем. Могут быть файлы csv с различным содержанием, поэтому мне нужно передать класс файла модели, чтобы я мог получить соответствующие объекты. Поскольку это чтение CSV-файла, я хотел сделать в общем. По тем или иным причинам ни одно из вышеперечисленных решений не помогло мне. Мне нужно использовать,
Class<? extends T> type
чтобы это работало. Я использую библиотеку opencsv для анализа файлов CSV.Вот как вызывается метод readFile
источник
Я использую обходной путь для этого:
источник