Aby móc programowo sprawdzić obiekt List i zobaczyć jego ogólny typ. Metoda może chcieć wstawiać obiekty w oparciu o ogólny typ kolekcji. Jest to możliwe w językach, które implementują generyczne w czasie wykonywania zamiast czasu kompilacji.
Steve Kuo,
4
Racja - jedynym sposobem na umożliwienie wykrycia środowiska wykonawczego jest podklasa: w rzeczywistości MOŻNA rozszerzyć typ ogólny, a następnie użyć deklaracji typu odbicia znalezionego, która została użyta. To trochę refleksji, ale możliwe. Niestety nie ma łatwego sposobu na wymuszenie, że należy użyć ogólnej podklasy.
StaxMan,
1
Z pewnością stringList zawiera ciągi i liczby całkowite? Po co to komplikować?
Ben Thurley,
Odpowiedzi:
408
Jeśli faktycznie są to pola pewnej klasy, możesz je uzyskać z niewielką pomocą refleksji:
Możesz to również zrobić dla typów parametrów i zwracanych metod.
Ale jeśli znajdują się w tym samym zakresie klasy / metody, w którym musisz o nich wiedzieć, to nie ma sensu ich znać, ponieważ już je sam zadeklarowałeś.
@Oleg Twoja zmienna używa <?>(typ wieloznaczny) zamiast <Class>(typ klasy). Wymień <?>By <Class>lub cokolwiek <E>.
BalusC
19
Możesz zrobić to samo dla parametrów metody:
Type[] types = method.getGenericParameterTypes();//Now assuming that the first parameter to the method is of type List<Integer>ParameterizedType pType =(ParameterizedType) types[0];Class<?> clazz =(Class<?>) pType.getActualTypeArguments()[0];System.out.println(clazz);//prints out java.lang.Integer
W method.getGenericParameterTypes (); jaka jest metoda
Neo Ravi
18
Krótka odpowiedź: nie.
To prawdopodobnie duplikat, nie można teraz znaleźć odpowiedniego.
Java używa czegoś o nazwie kasowanie typu, co oznacza, że w czasie wykonywania oba obiekty są równoważne. Kompilator wie, że listy zawierają liczby całkowite lub ciągi i jako takie mogą utrzymywać bezpieczne środowisko typu. Informacje te są tracone (na podstawie instancji obiektu) w czasie wykonywania, a lista zawiera tylko „Obiekty”.
Możesz dowiedzieć się trochę o klasie, jakie typy może być parametryzowane, ale zwykle jest to po prostu wszystko, co rozszerza „Obiekt”, tj. Cokolwiek. Jeśli zdefiniujesz typ jako
class<A extendsMyClass>AClass{....}
AClass.class będzie zawierał tylko fakt, że parametr A jest ograniczony przez MyClass, ale co więcej, nie można tego stwierdzić.
Będzie to prawdą, jeśli konkretna klasa ogólna nie zostanie określona; w swoim przykładzie wyraźnie deklaruje listy jako List<Integer>i List<String>; w takich przypadkach usunięcie typu nie ma zastosowania.
Haroldo_OK,
13
Ogólny typ kolekcji powinien mieć znaczenie tylko wtedy, gdy rzeczywiście zawiera w sobie obiekty, prawda? Czy nie jest łatwiej zrobić:
Collection<?> myCollection = getUnknownCollectionFromSomewhere();Class genericClass =null;Iterator it = myCollection.iterator();if(it.hasNext()){
genericClass = it.next().getClass();}if(genericClass !=null){//do whatever we needed to know the type for
W środowisku wykonawczym nie ma czegoś takiego jak rodzaj ogólny, ale na pewno obiekty wewnątrz środowiska wykonawczego są tego samego typu co deklarowany rodzaj ogólny, więc wystarczy przetestować klasę elementu przed przetworzeniem.
Inną rzeczą, którą możesz zrobić, to po prostu przetworzyć listę, aby uzyskać członków odpowiedniego typu, ignorując innych (lub przetwarzając ich inaczej).
Map<Class<?>,List<Object>> classObjectMap = myCollection.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(Object::getClass));// Process the list of the correct class, and/or handle objects of incorrect// class (throw exceptions, etc). You may need to group subclasses by// filtering the keys. For instance:List<Number> numbers = classObjectMap.entrySet().stream().filter(e->Number.class.isAssignableFrom(e.getKey())).flatMap(e->e.getValue().stream()).map(Number.class::cast).collect(Collectors.toList());
To da ci listę wszystkich przedmiotów, których klasy były podklasami, Numberktóre możesz następnie przetwarzać według potrzeb. Pozostałe elementy zostały odfiltrowane na inne listy. Ponieważ znajdują się na mapie, możesz je przetwarzać zgodnie z potrzebami lub ignorować.
Jeśli chcesz całkowicie zignorować elementy innych klas, staje się to znacznie prostsze:
A jeśli masz pustą kolekcję? A może masz kolekcję <Obiekt> z liczbą całkowitą i łańcuchem?
Falci
Umowa dla klasy może wymagać przekazania tylko list obiektów końcowych, a wywołanie metody zwróci null, jeśli lista jest pusta lub pusta lub jeśli typ listy jest nieprawidłowy.
ggb667,
1
W przypadku pustej kolekcji można bezpiecznie przypisać dowolny typ listy w czasie wykonywania, ponieważ nic w niej nie ma, a po usunięciu typu lista jest listą jest listą. Z tego powodu nie jestem w stanie wymyślić żadnego przypadku, w którym typ pustej kolekcji będzie miał znaczenie.
Steve K
Cóż, „może mieć znaczenie”, jeśli trzymasz referencję w wątku i przetwarzasz ją, gdy obiekty zaczną pojawiać się w scenariuszu konsumenta producenta. Jeśli lista zawiera niewłaściwe typy obiektów, mogą się zdarzyć złe rzeczy. Chyba nie pozwolę, byś musiał przerwać przetwarzanie, śpiąc, dopóki nie będzie co najmniej jednej pozycji na liście, zanim przejdziesz dalej.
ggb667,
Jeśli masz tego rodzaju scenariusz producenta / konsumenta, planowanie listy ma określony rodzaj ogólny, bez pewności, że to, co można by umieścić na liście, jest złe. Zamiast tego zadeklarujesz go jako zbiór Objects, a kiedy wyciągniesz je z listy, podasz je odpowiedniemu programowi obsługi na podstawie ich typu.
Jeśli chcesz uzyskać ogólny typ zwracanego typu, zastosowałem to podejście, gdy potrzebowałem znaleźć metody w klasie, która zwróciła a, Collectiona następnie uzyskać dostęp do ich ogólnych typów:
/**
* Performs a forced cast.
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/static<T>List<?super T> castListSafe(List<?> data,Class<T> listType){List<T> retval =null;//This test could be skipped if you trust the callers, but it wouldn't be safe then.if(data!=null&&!data.isEmpty()&& listType.isInstance(data.iterator().next().getClass())){@SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.List<T> foo =(List<T>)data;return retval;}return retval;}Usage:protectedWhateverClass add(List<?> data){//For fluant useageif(data==null)|| data.isEmpty(){thrownewIllegalArgumentException("add() "+ data==null?"null":"empty"+" collection");}Class<?> colType = data.iterator().next().getClass();//Something
aMethod(castListSafe(data, colType));}
aMethod(List<Foo> foo){for(Foo foo:List){System.out.println(Foo);}}
aMethod(List<Bar> bar){for(Bar bar:List){System.out.println(Bar);}}
Te same problemy, co w przypadku odpowiedzi Steve'a K: Co, jeśli lista jest pusta? Co jeśli lista jest typu <Obiekt>, który zawiera ciąg znaków i liczbę całkowitą? Twój kod oznacza, że możesz dodać więcej Ciągów, tylko jeśli pierwszy element na liście jest ciągiem i nie ma żadnych liczb całkowitych ...
subrunner
Jeśli lista jest pusta, nie masz szczęścia. Jeśli lista jest Obiektem i faktycznie zawiera Obiekt, wszystko jest w porządku, ale jeśli zawiera wiele rzeczy, nie masz szczęścia. Kasowanie oznacza, że jest to tak dobre, jak tylko możesz. Moim zdaniem brakuje skasowania. Konsekwencje tego są jednocześnie słabo zrozumiałe i niewystarczające do rozwiązania interesujących i pożądanych przypadków typowego zainteresowania. Nic nie stoi na przeszkodzie, aby stworzyć klasę, w której można zapytać, jakiego typu ona potrzebuje, ale po prostu nie tak działa klasa wbudowana. Kolekcje powinny wiedzieć, co zawierają, ale niestety ...
ggb667
4
W czasie wykonywania nie możesz.
Jednak poprzez odbicie parametry typu są dostępne. Próbować
for(Field field :this.getDeclaredFields()){System.out.println(field.getGenericType())}
Metoda getGenericType()zwraca obiekt typu. W tym przypadku będzie to instancja ParametrizedType, która z kolei ma metody getRawType()(które List.classw tym przypadku będą zawierać ) i getActualTypeArguments()która zwróci tablicę (w tym przypadku o długości 1, zawierającą albo String.classlub Integer.class).
Miałem ten sam problem, ale zamiast tego użyłem instanceof. Czy to w ten sposób:
List<Object> listCheck =(List<Object>)(Object) stringList;if(!listCheck.isEmpty()){if(listCheck.get(0)instanceofString){System.out.println("List type is String");}if(listCheck.get(0)instanceofInteger){System.out.println("List type is Integer");}}}
Wymaga to użycia niezaznaczonych rzutów, więc rób to tylko wtedy, gdy wiesz, że jest to lista i jaki może być typ.
Zasadniczo niemożliwe, ponieważ List<String>i List<Integer>współużytkują tę samą klasę wykonawczą.
Możesz jednak zastanowić się nad zadeklarowanym typem pola zawierającego listę (jeśli zadeklarowany typ sam nie odnosi się do parametru typu, którego wartości nie znasz).
Jak powiedzieli inni, jedyną prawidłową odpowiedzią jest nie, typ został usunięty.
Jeśli lista zawiera niezerową liczbę elementów, możesz zbadać typ pierwszego elementu (na przykład używając jego metody getClass). To nie powie ci o ogólnym typie listy, ale rozsądnie byłoby założyć, że typem ogólnym była jakaś nadklasa typów na liście.
Nie opowiadałbym się za tym podejściem, ale w przypadku powiązania może być przydatne.
Będzie to prawdą, jeśli konkretna klasa ogólna nie zostanie określona; w swoim przykładzie wyraźnie deklaruje listy jako List<Integer>i List<String>; w takich przypadkach usunięcie typu nie ma zastosowania.
Haroldo_OK,
2
import org.junit.Assert;import org.junit.Test;import java.lang.reflect.Field;import java.lang.reflect.ParameterizedType;import java.util.ArrayList;import java.util.Collection;import java.util.List;publicclassGenericTypeOfCollectionTest{publicclassFormBean{}publicclassMyClazz{privateList<FormBean> list =newArrayList<FormBean>();}@Testpublicvoid testName()throwsException{Field[] fields =MyClazz.class.getFields();for(Field field : fields){//1. Check if field is of Collection Typeif(Collection.class.isAssignableFrom(field.getType())){//2. Get Generic type of your fieldClass fieldGenericType = getFieldGenericType(field);//3. Compare with <FromBean>Assert.assertTrue("List<FormBean>",FormBean.class.isAssignableFrom(fieldGenericType));}}}//Returns generic type of any fieldpublicClass getFieldGenericType(Field field){if(ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())){ParameterizedType genericType =(ParameterizedType) field.getGenericType();return((Class)(genericType.getActualTypeArguments()[0])).getSuperclass();}//Returns dummy Boolean Class to compare with ValueObject & FormBeanreturnnewBoolean(false).getClass();}}
Będzie to prawdą, jeśli konkretna klasa ogólna nie zostanie określona; w swoim przykładzie wyraźnie deklaruje listy jako List<Integer>i List<String>; w takich przypadkach usunięcie typu nie ma zastosowania.
Haroldo_OK,
0
Użyj Reflection, aby uzyskać Fielddla nich, a następnie możesz po prostu: field.genericTypeuzyskać typ, który zawiera również informacje o ogólnych.
Odpowiedzi:
Jeśli faktycznie są to pola pewnej klasy, możesz je uzyskać z niewielką pomocą refleksji:
Możesz to również zrobić dla typów parametrów i zwracanych metod.
Ale jeśli znajdują się w tym samym zakresie klasy / metody, w którym musisz o nich wiedzieć, to nie ma sensu ich znać, ponieważ już je sam zadeklarowałeś.
źródło
<?>
(typ wieloznaczny) zamiast<Class>
(typ klasy). Wymień<?>
By<Class>
lub cokolwiek<E>
.Możesz zrobić to samo dla parametrów metody:
źródło
Krótka odpowiedź: nie.
To prawdopodobnie duplikat, nie można teraz znaleźć odpowiedniego.
Java używa czegoś o nazwie kasowanie typu, co oznacza, że w czasie wykonywania oba obiekty są równoważne. Kompilator wie, że listy zawierają liczby całkowite lub ciągi i jako takie mogą utrzymywać bezpieczne środowisko typu. Informacje te są tracone (na podstawie instancji obiektu) w czasie wykonywania, a lista zawiera tylko „Obiekty”.
Możesz dowiedzieć się trochę o klasie, jakie typy może być parametryzowane, ale zwykle jest to po prostu wszystko, co rozszerza „Obiekt”, tj. Cokolwiek. Jeśli zdefiniujesz typ jako
AClass.class będzie zawierał tylko fakt, że parametr A jest ograniczony przez MyClass, ale co więcej, nie można tego stwierdzić.
źródło
List<Integer>
iList<String>
; w takich przypadkach usunięcie typu nie ma zastosowania.Ogólny typ kolekcji powinien mieć znaczenie tylko wtedy, gdy rzeczywiście zawiera w sobie obiekty, prawda? Czy nie jest łatwiej zrobić:
W środowisku wykonawczym nie ma czegoś takiego jak rodzaj ogólny, ale na pewno obiekty wewnątrz środowiska wykonawczego są tego samego typu co deklarowany rodzaj ogólny, więc wystarczy przetestować klasę elementu przed przetworzeniem.
Inną rzeczą, którą możesz zrobić, to po prostu przetworzyć listę, aby uzyskać członków odpowiedniego typu, ignorując innych (lub przetwarzając ich inaczej).
To da ci listę wszystkich przedmiotów, których klasy były podklasami,
Number
które możesz następnie przetwarzać według potrzeb. Pozostałe elementy zostały odfiltrowane na inne listy. Ponieważ znajdują się na mapie, możesz je przetwarzać zgodnie z potrzebami lub ignorować.Jeśli chcesz całkowicie zignorować elementy innych klas, staje się to znacznie prostsze:
Możesz nawet utworzyć metodę narzędzia, aby upewnić się, że lista zawiera TYLKO te elementy, które pasują do określonej klasy:
źródło
Object
s, a kiedy wyciągniesz je z listy, podasz je odpowiedniemu programowi obsługi na podstawie ich typu.Aby znaleźć ogólny typ jednego pola:
źródło
Jeśli chcesz uzyskać ogólny typ zwracanego typu, zastosowałem to podejście, gdy potrzebowałem znaleźć metody w klasie, która zwróciła a,
Collection
a następnie uzyskać dostęp do ich ogólnych typów:To daje:
źródło
Rozwijając odpowiedź Steve'a K:
źródło
W czasie wykonywania nie możesz.
Jednak poprzez odbicie parametry typu są dostępne. Próbować
Metoda
getGenericType()
zwraca obiekt typu. W tym przypadku będzie to instancjaParametrizedType
, która z kolei ma metodygetRawType()
(któreList.class
w tym przypadku będą zawierać ) igetActualTypeArguments()
która zwróci tablicę (w tym przypadku o długości 1, zawierającą alboString.class
lubInteger.class
).źródło
method.getGenericParameterTypes()
aby uzyskać zadeklarowane typy parametrów metody.Miałem ten sam problem, ale zamiast tego użyłem instanceof. Czy to w ten sposób:
Wymaga to użycia niezaznaczonych rzutów, więc rób to tylko wtedy, gdy wiesz, że jest to lista i jaki może być typ.
źródło
Zasadniczo niemożliwe, ponieważ
List<String>
iList<Integer>
współużytkują tę samą klasę wykonawczą.Możesz jednak zastanowić się nad zadeklarowanym typem pola zawierającego listę (jeśli zadeklarowany typ sam nie odnosi się do parametru typu, którego wartości nie znasz).
źródło
Jak powiedzieli inni, jedyną prawidłową odpowiedzią jest nie, typ został usunięty.
Jeśli lista zawiera niezerową liczbę elementów, możesz zbadać typ pierwszego elementu (na przykład używając jego metody getClass). To nie powie ci o ogólnym typie listy, ale rozsądnie byłoby założyć, że typem ogólnym była jakaś nadklasa typów na liście.
Nie opowiadałbym się za tym podejściem, ale w przypadku powiązania może być przydatne.
źródło
List<Integer>
iList<String>
; w takich przypadkach usunięcie typu nie ma zastosowania.źródło
getFieldGenericTypefieldGenericType
nie można znaleźćTyp zostanie usunięty, więc nie będziesz mógł tego zrobić. Zobacz http://en.wikipedia.org/wiki/Type_erasure i http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
źródło
List<Integer>
iList<String>
; w takich przypadkach usunięcie typu nie ma zastosowania.Użyj Reflection, aby uzyskać
Field
dla nich, a następnie możesz po prostu:field.genericType
uzyskać typ, który zawiera również informacje o ogólnych.źródło