Chociaż prawdą jest, że Java usunie typy na runtime (zmieniając w ten sposób List<String>
w tylko List
), w wielu przypadkach rzeczywiście przechowuje typ ogólny w środowisku wykonawczym, umożliwiając przywrócenie utraconych informacji w celu ich usunięcia. można pobrać aktualne typy generyczne do nich:
- argumenty metody (danym przypadku)
- rodzajów powrotne Metoda
- typy pól
- Super klas/interfejsów
Oznacza to, jeśli po prostu masz obiekt typu List, nic nie możesz zrobić, aby uzyskać jego ogólny typ (object.getClass()
dostaniesz List
i to wszystko) - to był pe ricanently lost. Ale jeśli próbujesz ustalić typowy typ argumentu metody lub którykolwiek z powyższych, zwykle można uzyskać można za pomocą odbicia. Jak twoim przypadku nie wiąże zmienne typu lub inne komplikacje, to całkiem proste, aby uzyskać rzeczywisty typ:
ParameterizedType listArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", List.class).getGenericParameterTypes()[0];
Type listType = listArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the List
jeśli miał więcej parametrów i mapę Zamiast:
public String foo(Object bar, Map<String, Number> possibleFoos) { ... }
kodu, który być podobne:
ParameterizedType mapArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", Object.class, Map.class).getGenericParameterTypes()[1]; //1 is the index of the map argument
Type keyType = mapArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the Map key
Type valType = mapArgument.getActualTypeArguments()[1]; //This will be a Class representing Number, the type of the Map value
Można założyć, że jest to również metoda DWR, ponieważ typy są argumentami metod.są
podobne metody dostępne dla innych wymienionych przypadkach:
Class.getMethod(...).getGenericReturnType()
będzie Ci typ realny zwrot
Class.getField(fieldName).getGenericType()
będzie Ci prawdziwy typ pola
Class.getGenericSuperClass()
będzie Ci prawdziwą Super wpisz
Class.getGenericInterfaces()
otrzymasz prawdziwe typy interfejsów
Odpowiednie metody istnieją umożliwiając dostęp do AnnotatedType
(typ ogólny Plus adnotacje na wykorzystaniu typu) wprowadzony w Java 8:
Class.getMethod(...).getAnnotatedParameterTypes()
Class.getMethod(...).getAnnotatedReturnType()
Class.getField(fieldName).getAnnotatedType()
Class.getAnnotatedSuperClass()
Class.getAnnotatedInterfaces()
Teraz wszystko jest eleganckie, gdy sprawa jest tak prosta jak w przykładzie. Ale wyobraźcie sobie, że Twój przykład wygląda tak:
public T foo(List<T> possibleFoos) {...}
W tym przypadku getGenericParameterTypes()[0].getActualTypeArguments()[0]
nie daje T
co jest raczej bezużyteczne. Aby rozwiązać to, co oznacza T
, trzeba zajrzeć do definicji klasy i być może superklas, jednocześnie śledząc, w jaki sposób zmienne typu są nazywane w każdej klasie, ponieważ nazwy mogą być różne w każdym z nich.
Aby pracować z uniwersalnym typem refleksji łatwiejsze, można użyć wspaniała biblioteka nazywa GenTyRef że wykonuje pracę dla Ciebie, a jeśli potrzebujesz wsparcia dla AnnotatedType
s, można użyć mój widelec nazywa GeAnTyRef (oba są w Maven Centralny). Obejmują one również fabrykę typów, która umożliwia konstruowanie instancji o numerze (Annotated)Type
, której nie można łatwo wykonać przy użyciu zwykłego interfejsu API języka Java. Jest też poręczna implementacja super type token, która pozwala uzyskać literalny kod (Annotated)Type
.
z tymi, można zrobić wszystko z typów generycznych, że Java pozwala bez kłopotów wyjaśniłem powyżej:
GenericTypeReflector#getExactParameterTypes(...)
GenericTypeReflector#getExactReturnType(...)
GenericTypeReflector#getExactFieldType(...)
GenericTypeReflector#getExactSuperType(...)
I wiele innych operacji, takich jak figuri ng out, jeśli jeden Type
jest supertypem innego (podobnego do Class#isAssignableFrom
, ale dla typów ogólnych), rozróżniając zmienne określonego typu itp.
Dziękuję za wyczerpującą odpowiedź, dokładnie to chciałem wiedzieć! – Radim
Użyłem wersji "kłopotliwej" i działa ona dobrze dla listy, ale nie działa dla mapy, ponieważ getParameterizedType zwraca obiekt klasy.Ponieważ mam lepszy wgląd w twój poprzedni post, próbowałem ponownie szukać sposobu działania z mapą, ale niczego nie znalazłem. Czy możesz mi jeszcze raz pomóc? – Radim
@Radim Zaktualizowałem swoją odpowiedź, aby dołączyć przykład, który ma Mapę jako drugi argument. Czy tego właśnie szukasz? –
kaqqao