Ist es möglich, den Typ eines generischen Parameters abzurufen?
Ein Beispiel:
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
Cimnin
quelle
quelle
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
mir nicht sicher, was die Einschränkung ist.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Ich möchte versuchen, die Antwort von @DerMike aufzuschlüsseln, um Folgendes zu erklären:
Erstens bedeutet das Löschen des Typs nicht, dass das JDK eliminiert Typinformationen zur Laufzeit. Es ist eine Methode, mit der die Typprüfung zur Kompilierungszeit und die Laufzeitkompatibilität in derselben Sprache koexistieren können. Wie dieser Codeblock impliziert, behält das JDK die gelöschten Typinformationen bei - es ist nur nicht mit überprüften Casts und Sachen verbunden.
Zweitens liefert dies generische Typinformationen für eine generische Klasse genau eine Ebene höher als die Hierarchie des zu überprüfenden konkreten Typs - dh eine abstrakte übergeordnete Klasse mit generischen Typparametern kann die konkreten Typen finden, die ihren Typparametern für eine konkrete Implementierung von sich selbst entsprechen das erbt direkt davon. Wenn diese Klasse nicht abstrakt und instanziiert wäre oder die konkrete Implementierung zwei Ebenen tiefer wäre, würde dies nicht funktionieren (obwohl ein wenig Jimmying dazu führen könnte, dass sie auf eine vorgegebene Anzahl von Ebenen über eine oder bis zur niedrigsten Klasse angewendet wird mit X generischen Typparametern usw.).
Wie auch immer, weiter zur Erklärung. Hier ist noch einmal der Code, der zur leichteren Bezugnahme in Zeilen unterteilt ist:
Sei 'wir' die abstrakte Klasse mit generischen Typen, die diesen Code enthält. Lesen Sie dies grob von innen nach außen:
... und das war's auch schon. Wir übertragen also Typinformationen aus unserer eigenen konkreten Implementierung zurück in uns selbst und verwenden sie, um auf ein Klassenhandle zuzugreifen. Wir könnten getGenericSuperclass () verdoppeln und zwei Ebenen durchlaufen oder getGenericSuperclass () eliminieren und Werte für uns als konkreten Typ erhalten (Einschränkung: Ich habe diese Szenarien nicht getestet, sie sind noch nicht für mich aufgetaucht).
Es wird schwierig, wenn Ihre konkreten Kinder eine beliebige Anzahl von Sprüngen entfernt sind oder wenn Sie konkret und nicht endgültig sind, und besonders schwierig, wenn Sie erwarten, dass eines Ihrer (unterschiedlich tiefen) Kinder seine eigenen Generika hat. Normalerweise können Sie diese Überlegungen jedoch berücksichtigen, sodass Sie den größten Teil des Weges zurücklegen können.
Hoffe das hat jemandem geholfen! Ich erkenne, dass dieser Beitrag uralt ist. Ich werde diese Erklärung wahrscheinlich abschneiden und für andere Fragen aufbewahren.
quelle
Eigentlich habe ich das zum Laufen gebracht. Betrachten Sie das folgende Snippet:
Ich benutze JDK 1.6
quelle
Es gibt tatsächlich eine Lösung, indem der Trick "anonyme Klasse" und die Ideen aus den Super Type Tokens angewendet werden :
Der Trick liegt in der Schaffung einer anonymen Klasse ,
new ArrayList<SpiderMan>() {}
an der Stelle des ursprünglichen (einfach)new ArrayList<SpiderMan>()
. Die Verwendung einer anoymischen Klasse (falls möglich) stellt sicher, dass der Compiler Informationen über das Typargument behältSpiderMan
, das dem Typparameter gegeben wurdeList<?>
. Voilà!quelle
Aufgrund der Typlöschung besteht die einzige Möglichkeit, den Typ der Liste zu ermitteln, darin, den Typ als Parameter an die Methode zu übergeben:
quelle
Anhang zur Antwort von @ DerMike zum Abrufen des generischen Parameters einer parametrisierten Schnittstelle (mithilfe der Methode #getGenericInterfaces () in einer Java-8-Standardmethode, um Doppelarbeit zu vermeiden):
quelle
Awesomeable<Beer>
. In diesem Fall bleiben die Typinformationen erhalten. Wenn Sienew Awesomable<Beer> ()
an die Methode übergeben, funktioniert wt nicht.Awesomable<Beer>
on the fly ohne die explizite Definition einer konkreten Unterklasse wieEstrellaGalicia
in diesem Fall weitergeben, wird immer noch der parametrisierte Typ ausgegeben : Ich habe ihn gerade ausgeführt:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> Typ ist: ParameterizedStuff $ BeerNein, das ist nicht möglich. Aufgrund von Abwärtskompatibilitätsproblemen basieren die Generika von Java auf der Löschung von Typen , dh zur Laufzeit. Sie haben lediglich ein nicht generisches
List
Objekt. Es gibt einige Informationen zu Typparametern zur Laufzeit, diese befinden sich jedoch in Klassendefinitionen (dh Sie können fragen, welchen generischen Typ die Definition dieses Felds verwendet? ), Nicht in Objektinstanzen.quelle
Wie von @bertolami hervorgehoben, ist es uns nicht möglich, einen Variablentyp zu erhalten und seinen zukünftigen Wert (den Inhalt der Variablen typeOfList) abzurufen.
Trotzdem können Sie die Klasse wie folgt als Parameter übergeben:
Dies ist mehr oder weniger das, was Google tut, wenn Sie eine Klassenvariable an den Konstruktor von ActivityInstrumentationTestCase2 übergeben müssen .
quelle
Nein, das ist nicht möglich.
Sie können einen generischen Feldtyp erhalten, wenn eine Klasse die einzige Ausnahme von dieser Regel darstellt, und selbst das ist ein kleiner Hack.
Ein Beispiel hierfür finden Sie unter Kennen des generischen Typs in Java .
quelle
Sie können den Typ eines generischen Parameters mit Reflexion wie in diesem Beispiel erhalten, das ich hier gefunden habe :
quelle
Die schnelle Antwort auf die Frage lautet: Nein, das können Sie nicht, da der generische Java-Typ gelöscht wird.
Die längere Antwort wäre, wenn Sie Ihre Liste wie folgt erstellt haben:
In diesem Fall bleibt der generische Typ in der generischen Oberklasse der neuen anonymen Klasse oben erhalten.
Nicht, dass ich dies mit Listen empfehlen würde, aber es ist eine Listener-Implementierung:
Und da sich die Extrapolation der generischen Typen von Superklassen und Superschnittstellen zwischen JVMs ändert, ist die generische Lösung nicht so einfach, wie einige Antworten vermuten lassen.
Hier ist jetzt habe ich es getan.
quelle
Dies ist unmöglich, da Generika in Java nur zur Kompilierungszeit berücksichtigt werden. Somit sind die Java-Generika nur eine Art Vorprozessor. Sie können jedoch die tatsächliche Klasse der Mitglieder der Liste abrufen.
quelle
Ich habe dies für Methoden codiert, die erwarten, dass sie akzeptiert oder zurückgegeben werden
Iterable<?...>
. Hier ist der Code:quelle
Sie können keinen generischen Parameter aus einer Variablen abrufen. Sie können jedoch aus einer Methode oder Felddeklaration:
quelle
Nur für mich war das Lesen dieses Codeausschnitts schwierig, ich habe es nur in zwei lesbare Zeilen unterteilt:
Ich wollte eine Instanz des Type-Parameters erstellen, ohne Parameter für meine Methode zu haben:
quelle
Hier ist ein weiterer Trick. Verwenden Sie ein generisches Vararg-Array
quelle
Mir ist aufgefallen, dass sich viele Menschen zur
getGenericSuperclass()
Lösung neigen:Diese Lösung ist jedoch fehleranfällig. Es wird nicht richtig funktionieren, wenn die Nachkommen Generika enthalten. Bedenken Sie:
Welcher Typ wird
Bar.persistentClass
haben?Class<Integer>
? Nein, das wird es seinClass<Double>
. Dies geschieht, weilgetClass()
immer die oberste Klasse zurückgegeben wird, wasBar
in diesem Fall der Fall ist, und die generische Superklasse istFoo<Double>
. Daher wird der Argumenttyp seinDouble
.Wenn Sie eine zuverlässige Lösung benötigen, die nicht fehlschlägt, kann ich zwei vorschlagen.
Guava
. Es hat eine Klasse, die genau für diesen Zweck gemacht wurde :com.google.common.reflect.TypeToken
. Es behandelt alle Eckkoffer einwandfrei und bietet einige weitere nette Funktionen. Der Nachteil ist eine zusätzliche Abhängigkeit. Wenn Sie diese Klasse verwendet haben, sieht Ihr Code einfach und klar aus:quelle
Verwenden:
quelle
toArray()
kehrt zurückObject[]
. Sie können und sollten sich nicht darauf verlassen, dass es sich um einen bestimmten Subtyp handelt.