In einem kürzlich erschienenen Beitrag hat Arjan Blokzijl erörtert, wie Class.getGenericSuperclass verwendet werden kann, um auf generische Typinformationen zuzugreifen.
Das kann sehr nützlich sein, aber die Implementierung einer allgemeinen Methode zu diesem Zweck ist nicht so einfach, wie es vielleicht scheint. In diesem Beitrag werden wir einige der Probleme umreißen und sie hoffentlich lösen, indem wir eine Utility-Klasse erstellen. Denn mit etwas Glück ist dies die Art von Code, die nur einmal geschrieben werden muss! Für die Neugierigen oder Ungeduldigen, hier ist er.
Bevor wir in die Details eintauchen, sei darauf hingewiesen, dass dieses Dienstprogramm nur auf generische Typinformationen zugreifen kann, die in der Klassenhierarchie zur Kompilierzeit verfügbar sind; zum Beispiel wird der Agent in
[java]
class SecretAgentVehicle extends Vehicle<Agent>
[/java]
Die Java-Implementierung von Generika über Typ-Löschung bedeutet, dass es nicht möglich ist, auf Laufzeit-Typ-Informationen zuzugreifen - es gibt keine Möglichkeit keine Möglichkeit um den Agent von
[java]
Liste<Agent> list = new ArrayList<Agent>();
[/java]
da es weder in der Schnittstelle List noch in der Oberklasse AbstractList von ArrayList angegeben ist. . Ich würde mich übrigens gerne irren - wenn Sie einen cleveren Weg kennen, um auf diese Informationen zuzugreifen, lassen Sie es mich bitte wissen!
Wie Arjan gezeigt hat, sind die Informationen über generische Typen über ParameterizedType.getActualTypeArguments, wobei der ParameterizedType von Class.getGenericSuperclass oder Class.getGenericInterfaces zurückgegeben wird (Sie benötigen einen Cast von Type).
Dies funktioniert jedoch nur, wenn die übergeordnete Klasse eine generische Klasse ist (bzw. wenn die Klasse eine generische Schnittstelle implementiert). Wenn die übergeordnete Klasse eine "normale" Klasse ist, ist der zurückgegebene Type einfach das Ergebnis von Class.getSuperclass (bzw. Class.getInterfaces) und Sie erhalten eine ClassCastException, wenn Sie versuchen, auf ParameterizedType zu casten. Autsch!
Was ist also in einer Situation wie dieser zu tun?
[java]
class DoubleOhVehicle extends SecretAgentVehicle
[/java]
in der wir vermutlich noch wissen möchten, dass DoubleOhVehicle den Typparameter Agent hat? Nun, Sie müssen einfach in der Klassenhierarchie nach oben klettern, bis Sie die gewünschte Klasse erreichen, und eine Schleife wäre eine Möglichkeit, dies zu tun (eine rekursive Implementierung ist ebenfalls eine gute Lösung). Beachten Sie, dass wir davon ausgehen, dass die Eingabeklasse tatsächlich eine Unterklasse von SecretAgentVehicle ist, was z.B. mit gebundenen Typparametern erzwungen werden kann.
[java]
assert SecretAgentVehicle.class.isAssignableFrom(clazz);
Type supertype = clazz;
do {
supertype = supertype.getGenericSuperclass();
} while (!supertype.equals(Fahrzeug.class);
Type[] actualTypeArguments = ((ParameterizedType) supertype).getActualTypeArguments();
[/java]
Oder besser gesagt, es wäre schön, wenn Sie so etwas schreiben könnten...aber es wird nicht funktionieren. Leider verfügt Type nicht über eine getGenericSuperclass-Methode - für die Traversierung der Hierarchie müssen explizit Klasseninstanzen verwendet werden.
[java]
Class<? extends SecretAgentVehicle> clazz;
while (!clazz.equals(SecretAgentVehicle.class)) {
clazz = clazz.getSuperclass();
}
Type[] actualTypeArguments =
((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments();
[/java]
Voilà! Wir können jetzt mit Unterklassen umgehen. Was könnte es sonst noch zu tun geben?
Nun, betrachten Sie die folgende Klassenhierarchie:
[java]
Klasse Aktivität<U, V>
class SecretAgentActivity<S> extends Activity<Agent, S>
class SecretAgentMission extends SecretAgentActivity<Mission>
class BondMission extends SecretAgentMission
[/java]
Die (geordneten!) Typ-Parameter von BondMission sind natürlich Agent und Mission, aber
[java]
((ParameterizedType) SecretAgentActivity.class.getGenericSuperclass()).getActualTypeArguments();
[/java]
liefert nur [Agent, S]. So ärgerlich das auch sein mag, es ist in der Tat alles, was man erwarten kann:
Bis zu diesem Punkt haben wir uns nur auf Klassenhierarchien konzentriert. Aber bei so vielen generischen Schnittstellen wäre unser Dienstprogramm viel nützlicher, wenn die Unterklasse oder die Kontext-Oberklasse oder beide Schnittstellen sein könnten. Zum Beispiel für eine Gruppe von Klassen [java] interface AgentAttributes<V> extends Map<Agent, V> interface AgentCodenames extends AgentAttributes<String> class DigitCodenames implementiert AgentCodenames [/java] würden wir auch gerne beantworten können
- getActualTypeArguments(DigitCodeames.class, AgentAttributes.class) mit [String] und
- getActualTypeArguments(AgentCodeames.class, Map.class) mit [Agent, String].
Im Prinzip sollte dies nur geringfügige Änderungen erfordern. Eine davon ist, dass die Informationen zum
Verfasst von
Andrew Phillips
Contact



