Czy można uzyskać typ parametru generycznego?
Przykład:
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>());
}
}
java
generics
reflection
cimnine
źródło
źródło
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
nie jestem pewien, jakie jest ograniczenie.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Chcę spróbować rozbić odpowiedź z @DerMike, aby wyjaśnić:
Po pierwsze, wymazywanie typów nie oznacza, że JDK eliminuje informacje o typach w czasie wykonywania. Jest to metoda umożliwiająca sprawdzanie typów w czasie kompilacji i zgodność typów w środowisku wykonawczym w tym samym języku. Jak sugeruje ten blok kodu, JDK zachowuje usunięte informacje o typie - po prostu nie jest powiązany z zaznaczonymi rzutami i innymi rzeczami.
Po drugie, zapewnia to informacje o typie ogólnym klasie ogólnej dokładnie o jeden poziom w górę w hierarchii od sprawdzanego typu konkretnego - tj. Abstrakcyjna klasa nadrzędna z parametrami typu ogólnego może znaleźć konkretne typy odpowiadające jej parametrom typu w celu konkretnej implementacji samej siebie która bezpośrednio po niej dziedziczy. Gdyby ta klasa nie była abstrakcyjna i została utworzona, lub konkretna implementacja byłaby o dwa poziomy niższa, to nie zadziałałoby (chociaż odrobina jimmyingu mogłaby sprawić, że miałaby zastosowanie do dowolnej z góry określonej liczby poziomów powyżej jednego lub do najniższej klasy z X parametrami typu ogólnego i tak dalej).
W każdym razie do wyjaśnienia. Oto kod ponownie, podzielony na linie dla ułatwienia wyszukiwania:
Niech „my” będzie klasą abstrakcyjną z typami generycznymi zawierającymi ten kod. Czytając to z grubsza na lewą stronę:
... i to wszystko. Więc wypychamy informacje o typie z naszej własnej konkretnej implementacji z powrotem do siebie i używamy ich, aby uzyskać dostęp do uchwytu klasy. moglibyśmy podwoić getGenericSuperclass () i przejść o dwa poziomy lub wyeliminować getGenericSuperclass () i uzyskać wartości dla siebie jako konkretny typ (zastrzeżenie: nie testowałem tych scenariuszy, jeszcze nie pojawiły się dla mnie).
To staje się trudne, jeśli twoje konkretne dzieci są oddalone o dowolną liczbę przeskoków lub jeśli jesteś konkretny i nie ostateczny, a szczególnie trudny, jeśli oczekujesz, że którekolwiek z twoich (zmiennie głębokich) dzieci będzie miało własne rodzaje. Ale zwykle możesz projektować pod kątem tych rozważań, więc to daje Ci większość możliwości.
Mam nadzieję, że to komuś pomogło! Uznaję, że ten post jest starożytny. Prawdopodobnie wytnę to wyjaśnienie i zatrzymam na inne pytania.
źródło
Właściwie udało mi się to. Rozważ następujący fragment:
Używam jdk 1.6
źródło
Właściwie istnieje rozwiązanie, stosując sztuczkę „klasy anonimowej” i pomysły z tokenów supertypów :
Kłamstwa trick w sprawie utworzenia anonimowych klasy ,
new ArrayList<SpiderMan>() {}
w miejsce oryginału (proste)new ArrayList<SpiderMan>()
. Użycie anonimowej klasy (jeśli to możliwe) zapewnia, że kompilator zachowuje informacje o argumencie typuSpiderMan
podanym w parametrze typuList<?>
. Voilà!źródło
Ze względu na wymazywanie typów jedynym sposobem na poznanie typu listy byłoby przekazanie typu jako parametru do metody:
źródło
Dodatek do odpowiedzi @ DerMike na uzyskanie ogólnego parametru sparametryzowanego interfejsu (przy użyciu metody #getGenericInterfaces () wewnątrz domyślnej metody Java-8, aby uniknąć powielania):
źródło
Awesomeable<Beer>
. W takim przypadku informacje o typie są zachowane. Jeśli przejdziesznew Awesomable<Beer> ()
do metody, wt nie zadziała.Awesomable<Beer>
locie bez wyraźnej definicji konkretnej podklasy, jakEstrellaGalicia
w tym przypadku, nadal jest to sparametryzowany typ: Uruchomiłem go teraz:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> Typ to: ParameterizedStuff $ BeerNie, to niemożliwe. Ze względu na problemy ze zgodnością w dół, generyczne programy Java opierają się na wymazywaniu typów , między innymi w czasie wykonywania, wszystko co masz to
List
obiekt nieogólny . Istnieje kilka informacji o parametrach typu w czasie wykonywania, ale znajdują się one w definicjach klas (tj. Można zapytać „ jakiego typu generycznego używa definicja tego pola? ”), A nie w instancjach obiektów.źródło
Jak zaznaczył @bertolami, nie jest dla nas możliwy typ zmiennej i pobranie jej przyszłej wartości (zawartość zmiennej typeOfList).
Niemniej jednak możesz przekazać klasę jako parametr w ten sposób:
To mniej więcej to, co robi Google, gdy musisz przekazać zmienną klasy do konstruktora ActivityInstrumentationTestCase2 .
źródło
Nie, to niemożliwe.
Możesz uzyskać ogólny typ pola, biorąc pod uwagę, że klasa jest jedynym wyjątkiem od tej reguły, a nawet to trochę hack.
Zobacz Znajomość typów ogólnych w Javie, aby zobaczyć przykład.
źródło
Możesz uzyskać typ parametru generycznego z odbiciem, jak w tym przykładzie, który znalazłem tutaj :
źródło
Szybka odpowiedź na pytanie brzmi: nie, nie możesz, z powodu wymazywania ogólnego typu Java.
Dłuższą odpowiedzią byłoby to, że jeśli utworzyłeś swoją listę w ten sposób:
W takim przypadku typ ogólny jest zachowywany w nadklasie ogólnej nowej klasy anonimowej powyżej.
Nie żebym zalecał robienie tego z listami, ale jest to implementacja słuchacza:
A ponieważ ekstrapolacja ogólnych typów superklas i super interfejsów zmienia się między maszynami JVM, ogólne rozwiązanie nie jest tak proste, jak mogą sugerować niektóre odpowiedzi.
Oto teraz zrobiłem to.
źródło
Jest to niemożliwe, ponieważ typy generyczne w Javie są uwzględniane tylko w czasie kompilacji. W związku z tym typy generyczne języka Java to tylko pewnego rodzaju preprocesor. Możesz jednak uzyskać aktualną klasę członków listy.
źródło
Zakodowałem to dla metod, które oczekują akceptacji lub zwrotu
Iterable<?...>
. Oto kod:źródło
Nie można pobrać ogólnego parametru ze zmiennej. Ale możesz z metody lub deklaracji pola:
źródło
Czytanie tego fragmentu kodu było trudne, po prostu podzieliłem go na 2 czytelne wiersze:
Chciałem utworzyć wystąpienie parametru Type bez żadnych parametrów mojej metody:
źródło
Oto kolejna sztuczka. Użyj ogólnej tablicy vararg
źródło
Zauważyłem, że wiele osób skłania się ku
getGenericSuperclass()
rozwiązaniu:Jednak to rozwiązanie jest podatne na błędy. To będzie nie działać prawidłowo, jeśli są leki generyczne w potomkom. Rozważ to:
Który typ będzie
Bar.persistentClass
miał?Class<Integer>
? Nie, będzieClass<Double>
. Stanie się tak, ponieważgetClass()
zawsze zwraca najwyższą najwyższą klasę, co jestBar
w tym przypadku, a jego ogólna super klasa takFoo<Double>
. Stąd typ argumentu będzieDouble
.Jeśli potrzebujesz niezawodnego rozwiązania, które nie zawodzi, mogę zaproponować dwa.
Guava
. Ma klasę, która została wykonana dokładnie do tego celu:com.google.common.reflect.TypeToken
. Doskonale radzi sobie ze wszystkimi narożnikami i oferuje bardziej przyjemną funkcjonalność. Wadą jest dodatkowa zależność. Biorąc pod uwagę, że użyłeś tej klasy, Twój kod wyglądałby prosto i przejrzyście, na przykład:źródło
Posługiwać się:
źródło
toArray()
zwracaObject[]
. Nie możesz i nie powinieneś polegać na tym, że jest to jakiś szczególny podtyp.