import com.fasterxml.jackson.core.type.TypeReference; new TypeReference<T>(){}
Bogdan Shulga
Odpowiedzi:
569
Krótka odpowiedź brzmi, że nie ma sposobu, aby dowiedzieć się, jakie parametry wykonawcze mają ogólne parametry typu w Javie. Sugeruję przeczytanie rozdziału o usuwaniu czcionek w samouczku Java, aby uzyskać więcej informacji.
Popularnym rozwiązaniem tego jest przekazanie Classparametru typu do konstruktora typu ogólnego, np
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}}
Ta odpowiedź stanowi prawidłowe rozwiązanie, ale niedokładne jest stwierdzenie, że nie można znaleźć typu ogólnego w czasie wykonywania. Okazuje się, że kasowanie typu jest o wiele bardziej skomplikowane niż kasowanie ogólne. Moja odpowiedź pokazuje, jak uzyskać ogólny typ zajęć.
Ben Thurley,
3
@BenThurley Neat trick, ale o ile widzę, działa tylko, jeśli istnieje ogólny nadtyp, którego można użyć. W moim przykładzie nie można pobrać typu T w Foo <T>.
Zsolt Török
@webjockey Nie, nie powinieneś. Przypisanie typeParameterClassdomyślnego przypisania do konstruktora jest w porządku. Nie ma potrzeby ustawiania go po raz drugi.
Adowrath,
To pierwsze rozwiązanie, które przychodzi na myśl, ale czasami to nie Ty będziesz tworzył / inicjował obiekty. Nie będziesz mógł używać konstruktora. Na przykład podczas pobierania jednostek JPA z bazy danych.
Paramvir Singh Karwal
234
Szukałem sposobu na zrobienie tego sam, bez dodawania dodatkowej zależności do ścieżki klas. Po pewnym dochodzeniu stwierdziłem, że jest to możliwe, dopóki masz ogólny nadtyp. Było to dla mnie OK, ponieważ pracowałem z warstwą DAO z nadtypem warstwy ogólnej. Jeśli to pasuje do twojego scenariusza, to jest to najładniejsze podejście IMHO.
Większość ogólnych przypadków użycia, z którymi się spotkałem, ma jakiś rodzaj ogólny, np. List<T>Dla ArrayList<T>lub GenericDAO<T>dlaDAO<T> itp.
Mój projekt wykorzystywał Spring, co jest jeszcze lepsze, ponieważ Spring ma przydatną metodę użyteczną do znalezienia tego typu. Jest to dla mnie najlepsze podejście, ponieważ wygląda najładniej. Myślę, że gdybyś nie używał Springa, mógłbyś napisać własną metodę narzędzia.
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 ";}
proszę wyjaśnić sens resolveTypeArgument argumentów
gstackoverflow
getClass () jest metodą java.lang.Object, która zwróci klasę określonego obiektu w czasie wykonywania, jest to obiekt, dla którego chcesz rozwiązać typ. Klasa AbstractHibernateEXE.class to tylko nazwa klasy bazowej lub nadklasy ogólnej hierarchii klas typów. Dołączona jest instrukcja importu, więc powinieneś być w stanie łatwo znaleźć dokumenty i sprawdzić się. To jest strona docs.spring.io/spring/docs/current/javadoc-api/org/…
Ben Thurley
Co to jest rozwiązanie sprężynowe w wersji 4.3.6 i wyższej. Nie działa ze sprężyną 4.3.6.
@ AlikElzin-kilaka zostaje zainicjowany w konstruktorze za pomocą klasy Spring GenericTypeResolver.
Ben Thurley,
103
Istnieje jednak niewielka luka: jeśli zdefiniujesz swoją Fooklasę jako abstrakcyjną. Oznaczałoby to, że musisz stworzyć swoją klasę jako:
Foo<MyType> myFoo =newFoo<MyType>(){};
(Zwróć uwagę na podwójne nawiasy klamrowe na końcu.)
Teraz możesz pobrać typ Tw czasie wykonywania:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();Type tType =((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Zauważ jednak, że mySuperclassmusi to być nadklasa definicji klasy, która faktycznie określa ostateczny typ T.
Nie jest również zbyt elegancki, ale musisz zdecydować, czy wolisz, new Foo<MyType>(){}czy new Foo<MyType>(MyType.class);w swoim kodzie.
Na przykład:
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;}}}
Następnie:
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 );}}
To jest najlepsza odpowiedź tutaj! Co do wartości, jest to strategia, którą Google Guice stosuje do wiązania klas zTypeLiteral
ATG
14
Zauważ, że za każdym razem, gdy używana jest ta metoda konstrukcji obiektu, tworzona jest nowa anonimowa klasa. Innymi słowy, dwa obiekty ai butworzone w ten sposób będą rozszerzać tę samą klasę, ale nie będą miały identycznych klas instancji. a.getClass() != b.getClass()
Martin Serrano,
3
Jest jeden scenariusz, w którym to nie działa. Jeśli Foo powinien zaimplementować interfejs, taki jak Serializable, anonimowa klasa nie byłaby Serializable, chyba że tworzenie instancji klasy jest. Próbowałem to obejść, tworząc możliwą do serializacji klasę fabryczną, która tworzy anonimową klasę pochodzącą od Foo, ale z jakiegoś powodu getActualTypeArguments zwraca typ ogólny zamiast faktycznej klasy. Na przykład: (new FooFactory <MyType> ()). CreateFoo ()
Lior Chaga
38
Standardowym podejściem / obejściem / rozwiązaniem jest dodanie classobiektu do konstruktora (-ów), na przykład:
publicclassFoo<T>{privateClass<T> type;publicFoo(Class<T> type){this.type = type;}publicClass<T> getType(){return type;}public T newInstance(){return type.newInstance();}}
Ale wydawało się, że nie można @autowired w rzeczywistym użyciu, w jakikolwiek sposób obejść?
Alfred Huang
@AlfredHuang Obejściem problemu będzie stworzenie fasoli dla klasy, która to robi i nie będzie polegać na automatycznym zasilaniu.
Calebj
20
Wyobraź sobie, że masz abstrakcyjną nadklasę, która jest ogólna:
publicabstractclassFoo<?extends T>{}
A potem masz drugą klasę, która rozszerza Foo za pomocą ogólnego paska, który rozszerza T:
publicclassSecondextendsFoo<Bar>{}
Możesz uzyskać klasę Bar.classw klasie Foo, wybierając Type(z odpowiedzi bert bruynooghe) i wnioskując ją za pomocą Classinstancji:
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);
Trzeba pamiętać, że ta operacja nie jest idealna, dlatego dobrym pomysłem jest buforowanie obliczonej wartości, aby uniknąć wielokrotnych obliczeń. Jednym z typowych zastosowań jest ogólna implementacja DAO.
toString().split(" ")[1]to był problem, unikaj"class "
IgniteCoders
16
Oto działające rozwiązanie:
@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 <> ");}}
UWAGI:
Może być używany tylko jako nadklasa
Musi zostać rozszerzony o typ klasowy ( Child extends Generic<Integer>)
LUB
Musi zostać utworzony jako anonimowa implementacja ( new Generic<Integer>() {};)
Lepszą drogą niż klasą, którą sugerowali inni, jest przejście do obiektu, który może zrobić to, co zrobiłbyś z klasą, np. Utworzyć nową instancję.
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>());
@ Ricky Clarkson: Nie rozumiem, jak ta fabryka powinna zwracać sparametryzowane pianki. Czy mógłbyś wyjaśnić, w jaki sposób można uzyskać z tego Foo <T>? Wydaje mi się, że daje to tylko niesparametryzowane Foo. Czy T w make10 nie jest po prostu Foo?
ib84 13.03.13
@ ib84 Naprawiłem kod; Wydaje mi się, że przegapiłem to, że Foo zostało sparametryzowane, kiedy pierwotnie napisałem odpowiedź.
Ricky Clarkson
9
Miałem ten problem w abstrakcyjnej klasie ogólnej. W tym konkretnym przypadku rozwiązanie jest prostsze:
Tak, ale chciałbym pozostawić bardzo minimum, które należy zrobić, rozszerzając tę klasę. Sprawdź odpowiedź
droidpl
5
Mam (brzydkie, ale skuteczne) rozwiązanie tego problemu, z którego ostatnio korzystałem:
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 __(){}}
Znalazłem ogólny i prosty sposób na zrobienie tego. W mojej klasie stworzyłem metodę, która zwraca typ ogólny zgodnie z jego pozycją w definicji klasy. Załóżmy taką definicję klasy:
publicclassMyClass<A, B, C>{}
Teraz utwórzmy niektóre atrybuty, aby zachować typy:
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)}
Następnie możesz utworzyć ogólną metodę, która zwraca typ na podstawie indeksu ogólnej definicji:
/**
* 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];}
Na koniec w konstruktorze wystarczy wywołać metodę i wysłać indeks dla każdego typu. Cały kod powinien wyglądać następująco:
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];}}
Jak wyjaśniono w innych odpowiedziach, użyj tego ParameterizedType podejście, musisz rozszerzyć klasę, ale wydaje się to dodatkową pracą, aby stworzyć zupełnie nową klasę, która ją rozszerza ...
Tak więc abstrakcja klasy zmusza cię do jej rozszerzenia, spełniając w ten sposób wymóg podklasy. (używając @Getter lomboka).
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}}
Jeśli rozszerzasz lub implementujesz dowolną klasę / interfejs, który używa ogólnych, możesz uzyskać Ogólny typ klasy / interfejsu nadrzędnego, bez modyfikowania żadnej istniejącej klasy / interfejsu.
Mogą być trzy możliwości,
Przypadek 1
Gdy twoja klasa rozszerza klasę, która używa Generics
To całkiem proste. Jeśli potrzebujesz z tej samej klasy:
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());}
Właściwie przypuszczam, że masz pole w swojej klasie typu T. Jeśli nie ma pola typu T, po co mieć ogólny typ? Możesz więc po prostu zrobić instancję na tym polu.
W moim przypadku mam
Wyświetl elementy <T>;
w mojej klasie i sprawdzam, czy typem klasy jest „Lokalizacja” wg
if (items.get (0) instanceof Locality) ...
Oczywiście działa to tylko wtedy, gdy całkowita liczba możliwych klas jest ograniczona.
classGenericClass<T>(private val rawType:Class<*>){
constructor():this(`$Gson$Types`.getRawType(object :TypeToken<T>(){}.getType()))
fun getRawType():Class<T>{return rawType as Class<T>}}
Chciałem przekazać T.classowi metodę, która korzysta z Generics
Metoda readFile czyta plik .csv określony przez fileName z pełną ścieżką. Mogą istnieć pliki csv o różnej zawartości, dlatego muszę przekazać klasę pliku modelu, aby uzyskać odpowiednie obiekty. Ponieważ to czyta plik csv, chciałem zrobić w ogólny sposób. Z tego czy innego powodu żadne z powyższych rozwiązań nie działało dla mnie. Muszę użyć,
Class<? extends T> typeżeby to działało. Używam biblioteki opencsv do parsowania plików CSV.
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
Odpowiedzi:
Krótka odpowiedź brzmi, że nie ma sposobu, aby dowiedzieć się, jakie parametry wykonawcze mają ogólne parametry typu w Javie. Sugeruję przeczytanie rozdziału o usuwaniu czcionek w samouczku Java, aby uzyskać więcej informacji.
Popularnym rozwiązaniem tego jest przekazanie
Class
parametru typu do konstruktora typu ogólnego, npźródło
typeParameterClass
domyślnego przypisania do konstruktora jest w porządku. Nie ma potrzeby ustawiania go po raz drugi.Szukałem sposobu na zrobienie tego sam, bez dodawania dodatkowej zależności do ścieżki klas. Po pewnym dochodzeniu stwierdziłem, że jest to możliwe, dopóki masz ogólny nadtyp. Było to dla mnie OK, ponieważ pracowałem z warstwą DAO z nadtypem warstwy ogólnej. Jeśli to pasuje do twojego scenariusza, to jest to najładniejsze podejście IMHO.
Większość ogólnych przypadków użycia, z którymi się spotkałem, ma jakiś rodzaj ogólny, np.
List<T>
DlaArrayList<T>
lubGenericDAO<T>
dlaDAO<T>
itp.Rozwiązanie Pure Java
Artykuł Uzyskiwanie dostępu do typów ogólnych w środowisku wykonawczym w Javie wyjaśnia, jak to zrobić za pomocą czystej Java.
Wiosenne rozwiązanie
Mój projekt wykorzystywał Spring, co jest jeszcze lepsze, ponieważ Spring ma przydatną metodę użyteczną do znalezienia tego typu. Jest to dla mnie najlepsze podejście, ponieważ wygląda najładniej. Myślę, że gdybyś nie używał Springa, mógłbyś napisać własną metodę narzędzia.
źródło
Istnieje jednak niewielka luka: jeśli zdefiniujesz swoją
Foo
klasę jako abstrakcyjną. Oznaczałoby to, że musisz stworzyć swoją klasę jako:(Zwróć uwagę na podwójne nawiasy klamrowe na końcu.)
Teraz możesz pobrać typ
T
w czasie wykonywania:Zauważ jednak, że
mySuperclass
musi to być nadklasa definicji klasy, która faktycznie określa ostateczny typT
.Nie jest również zbyt elegancki, ale musisz zdecydować, czy wolisz,
new Foo<MyType>(){}
czynew Foo<MyType>(MyType.class);
w swoim kodzie.Na przykład:
Następnie:
źródło
TypeLiteral
a
ib
utworzone w ten sposób będą rozszerzać tę samą klasę, ale nie będą miały identycznych klas instancji.a.getClass() != b.getClass()
Standardowym podejściem / obejściem / rozwiązaniem jest dodanie
class
obiektu do konstruktora (-ów), na przykład:źródło
Wyobraź sobie, że masz abstrakcyjną nadklasę, która jest ogólna:
A potem masz drugą klasę, która rozszerza Foo za pomocą ogólnego paska, który rozszerza T:
Możesz uzyskać klasę
Bar.class
w klasie Foo, wybierającType
(z odpowiedzi bert bruynooghe) i wnioskując ją za pomocąClass
instancji:Trzeba pamiętać, że ta operacja nie jest idealna, dlatego dobrym pomysłem jest buforowanie obliczonej wartości, aby uniknąć wielokrotnych obliczeń. Jednym z typowych zastosowań jest ogólna implementacja DAO.
Ostateczne wdrożenie:
Zwracana wartość to Bar.class po wywołaniu z klasy Foo w innej funkcji lub z klasy Bar.
źródło
toString().split(" ")[1]
to był problem, unikaj"class "
Oto działające rozwiązanie:
UWAGI: Może być używany tylko jako nadklasa
Child extends Generic<Integer>
)LUB
new Generic<Integer>() {};
)źródło
Nie możesz tego zrobić z powodu usunięcia typu. Zobacz także Pytanie o przepełnienie stosu Generics Java - usuwanie typu - kiedy i co się dzieje .
źródło
Lepszą drogą niż klasą, którą sugerowali inni, jest przejście do obiektu, który może zrobić to, co zrobiłbyś z klasą, np. Utworzyć nową instancję.
źródło
Miałem ten problem w abstrakcyjnej klasie ogólnej. W tym konkretnym przypadku rozwiązanie jest prostsze:
a później klasa pochodna:
źródło
Mam (brzydkie, ale skuteczne) rozwiązanie tego problemu, z którego ostatnio korzystałem:
źródło
To jest możliwe:
Potrzebujesz dwóch funkcji z hibernacji-generic-dao / blob / master / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java .
Aby uzyskać więcej wyjaśnień, zobacz Odbicie ogólnych .
źródło
Znalazłem ogólny i prosty sposób na zrobienie tego. W mojej klasie stworzyłem metodę, która zwraca typ ogólny zgodnie z jego pozycją w definicji klasy. Załóżmy taką definicję klasy:
Teraz utwórzmy niektóre atrybuty, aby zachować typy:
Następnie możesz utworzyć ogólną metodę, która zwraca typ na podstawie indeksu ogólnej definicji:
Na koniec w konstruktorze wystarczy wywołać metodę i wysłać indeks dla każdego typu. Cały kod powinien wyglądać następująco:
źródło
Jak wyjaśniono w innych odpowiedziach, użyj tego
ParameterizedType
podejście, musisz rozszerzyć klasę, ale wydaje się to dodatkową pracą, aby stworzyć zupełnie nową klasę, która ją rozszerza ...Tak więc abstrakcja klasy zmusza cię do jej rozszerzenia, spełniając w ten sposób wymóg podklasy. (używając @Getter lomboka).
Teraz, aby go rozszerzyć bez definiowania nowej klasy. (Uwaga {} na końcu ... rozszerzone, ale nie zastępuj niczego - chyba że chcesz).
źródło
Zakładam, że skoro masz klasę ogólną, miałbyś taką zmienną:
(ta zmienna musi przyjąć wartość w konstruktorze)
W takim przypadku możesz po prostu utworzyć następującą metodę:
Mam nadzieję, że to pomoże!
źródło
źródło
Jeśli rozszerzasz lub implementujesz dowolną klasę / interfejs, który używa ogólnych, możesz uzyskać Ogólny typ klasy / interfejsu nadrzędnego, bez modyfikowania żadnej istniejącej klasy / interfejsu.
Mogą być trzy możliwości,
Przypadek 1 Gdy twoja klasa rozszerza klasę, która używa Generics
Przypadek 2 Gdy twoja klasa implementuje interfejs korzystający z Generics
Przypadek 3 Gdy interfejs rozszerza interfejs korzystający z Generics
źródło
To całkiem proste. Jeśli potrzebujesz z tej samej klasy:
źródło
Właściwie przypuszczam, że masz pole w swojej klasie typu T. Jeśli nie ma pola typu T, po co mieć ogólny typ? Możesz więc po prostu zrobić instancję na tym polu.
W moim przypadku mam
w mojej klasie i sprawdzam, czy typem klasy jest „Lokalizacja” wgOczywiście działa to tylko wtedy, gdy całkowita liczba możliwych klas jest ograniczona.
źródło
To pytanie jest stare, ale teraz najlepsze jest korzystanie z Google
Gson
.Przykład uzyskania niestandardowego
viewModel
.Ogólna klasa typów
źródło
Chciałem przekazać T.classowi metodę, która korzysta z Generics
Metoda readFile czyta plik .csv określony przez fileName z pełną ścieżką. Mogą istnieć pliki csv o różnej zawartości, dlatego muszę przekazać klasę pliku modelu, aby uzyskać odpowiednie obiekty. Ponieważ to czyta plik csv, chciałem zrobić w ogólny sposób. Z tego czy innego powodu żadne z powyższych rozwiązań nie działało dla mnie. Muszę użyć,
Class<? extends T> type
żeby to działało. Używam biblioteki opencsv do parsowania plików CSV.W ten sposób wywoływana jest metoda readFile
źródło
Korzystam z obejścia tego:
źródło