Wie geht man vor und versucht, alle Unterklassen einer bestimmten Klasse (oder alle Implementierer einer bestimmten Schnittstelle) in Java zu finden? Ab sofort habe ich eine Methode, um dies zu tun, aber ich finde es ziemlich ineffizient (gelinde gesagt). Die Methode ist:
- Ruft eine Liste aller Klassennamen ab, die im Klassenpfad vorhanden sind
- Laden Sie jede Klasse und testen Sie, ob es sich um eine Unterklasse oder einen Implementierer der gewünschten Klasse oder Schnittstelle handelt
In Eclipse gibt es eine nette Funktion namens Typhierarchie, die es schafft, dies recht effizient darzustellen. Wie geht man vor und macht es programmatisch?
Antworten:
Es gibt keinen anderen Weg, als den von Ihnen beschriebenen. Denken Sie darüber nach - wie kann jemand wissen, welche Klassen ClassX erweitern, ohne jede Klasse im Klassenpfad zu scannen?
Eclipse kann Sie nur in einer scheinbar "effizienten" Zeitspanne über die Super- und Unterklassen informieren, da bereits alle Typdaten an dem Punkt geladen sind, an dem Sie auf die Schaltfläche "In Typhierarchie anzeigen" klicken (da dies der Fall ist) Sie kompilieren ständig Ihre Klassen, wissen alles über den Klassenpfad usw.).
quelle
reflections.getSubTypesOf(aClazz))
LinkDas Scannen nach Klassen ist mit reinem Java nicht einfach.
Das Spring Framework bietet eine Klasse namens ClassPathScanningCandidateComponentProvider , die genau das tun kann, was Sie benötigen. Im folgenden Beispiel werden alle Unterklassen von MyClass im Paket org.example.package gefunden
Diese Methode hat den zusätzlichen Vorteil, dass ein Bytecode-Analysator verwendet wird, um die Kandidaten zu finden, was bedeutet, dass nicht alle gescannten Klassen geladen werden.
quelle
Dies ist nicht möglich, wenn nur die integrierte Java Reflections-API verwendet wird.
Es gibt ein Projekt, das das erforderliche Scannen und Indizieren Ihres Klassenpfads durchführt, damit Sie auf diese Informationen zugreifen können ...
Reflexionen
(Haftungsausschluss: Ich habe es nicht verwendet, aber die Projektbeschreibung scheint genau Ihren Anforderungen zu entsprechen.)
quelle
Vergessen Sie nicht, dass das generierte Javadoc für eine Klasse eine Liste bekannter Unterklassen enthält (und für Schnittstellen bekannte implementierende Klassen).
quelle
Versuchen Sie ClassGraph . (Haftungsausschluss, ich bin der Autor). ClassGraph unterstützt das Scannen nach Unterklassen einer bestimmten Klasse, entweder zur Laufzeit oder zur Erstellungszeit, aber auch viel mehr. ClassGraph kann eine abstrakte Darstellung des gesamten Klassendiagramms (alle Klassen, Anmerkungen, Methoden, Methodenparameter und Felder) im Speicher, für alle Klassen im Klassenpfad oder für Klassen in Paketen mit Whitelist erstellen, und Sie können dieses Klassendiagramm jedoch abfragen Sie wollen. ClassGraph unterstützt mehr Klassenpfadspezifikationsmechanismen und Klassenladeprogramme als jeder andere Scanner und funktioniert auch nahtlos mit dem neuen JPMS-Modulsystem. Wenn Sie Ihren Code also auf ClassGraph basieren, ist Ihr Code maximal portabel. Siehe die API hier.
quelle
Ich habe das vor einigen Jahren gemacht. Der zuverlässigste Weg, dies zu tun (dh mit offiziellen Java-APIs und ohne externe Abhängigkeiten), besteht darin, ein benutzerdefiniertes Doclet zu schreiben, um eine Liste zu erstellen, die zur Laufzeit gelesen werden kann.
Sie können es wie folgt über die Befehlszeile ausführen:
oder führen Sie es von Ameise wie folgt aus:
Hier ist der Grundcode:
Der Einfachheit halber habe ich das Parsen von Befehlszeilenargumenten entfernt und schreibe eher in System.out als in eine Datei.
quelle
Ich weiß, dass ich ein paar Jahre zu spät zu dieser Party komme, aber ich bin auf diese Frage gestoßen, um das gleiche Problem zu lösen. Sie können die interne Suche von Eclipse programmgesteuert verwenden, wenn Sie ein Eclipse-Plugin schreiben (und somit dessen Caching usw. nutzen), um Klassen zu finden, die eine Schnittstelle implementieren. Hier ist mein (sehr grober) erster Schnitt:
Das erste Problem, das ich bisher sehe, ist, dass ich nur Klassen abfange, die die Schnittstelle direkt implementieren, nicht alle ihre Unterklassen - aber eine kleine Rekursion hat niemanden verletzt.
quelle
Unter Berücksichtigung der in den anderen Antworten genannten Einschränkungen können Sie openpojo's
PojoClassFactory
( verfügbar bei Maven ) auch folgendermaßen verwenden:Wo
packageRoot
befindet sich die Stammzeichenfolge der Pakete, in denen Sie suchen möchten (z. B."com.mycompany"
oder auch nur"com"
), undSuperclass
Ihr Supertyp (dies funktioniert auch auf Schnittstellen).quelle
Ich schreibe nur eine einfache Demo, um die
org.reflections.Reflections
zu verwenden, um Unterklassen der abstrakten Klasse zu erhalten:https://github.com/xmeng1/ReflectionsDemo
quelle
Abhängig von Ihren speziellen Anforderungen kann der Service Loader- Mechanismus von Java in einigen Fällen das erreichen, wonach Sie suchen.
Kurz gesagt, Entwickler können explizit deklarieren, dass eine Klasse eine andere Klasse unterklassifiziert (oder eine Schnittstelle implementiert), indem sie diese in einer Datei im
META-INF/services
Verzeichnis der JAR / WAR-Datei auflistet . Es kann dann mithilfe derjava.util.ServiceLoader
Klasse ermittelt werden, die bei Angabe einesClass
Objekts Instanzen aller deklarierten Unterklassen dieser Klasse generiert (oder, wenn diesClass
eine Schnittstelle darstellt, aller Klassen, die diese Schnittstelle implementieren).Der Hauptvorteil dieses Ansatzes besteht darin, dass nicht der gesamte Klassenpfad manuell nach Unterklassen durchsucht werden muss. Die gesamte Erkennungslogik ist in der
ServiceLoader
Klasse enthalten und es werden nur die Klassen geladen, die explizit imMETA-INF/services
Verzeichnis deklariert sind (nicht jede Klasse im Klassenpfad). .Es gibt jedoch einige Nachteile:
META-INF/services
Verzeichnis explizit deklarieren . Dies ist eine zusätzliche Belastung für den Entwickler und kann fehleranfällig sein.ServiceLoader.iterator()
generiert Unterklasseninstanzen, nicht derenClass
Objekte. Dies verursacht zwei Probleme:Anscheinend wird Java 9 einige dieser Mängel beheben (insbesondere diejenigen, die die Instanziierung von Unterklassen betreffen).
Ein Beispiel
Angenommen, Sie möchten Klassen finden, die eine Schnittstelle implementieren
com.example.Example
:Die Klasse
com.example.ExampleImpl
implementiert diese Schnittstelle:Sie würden deklarieren, dass die Klasse
ExampleImpl
eine Implementierung ist,Example
indem Sie eine Datei erstellen,META-INF/services/com.example.Example
die den Text enthältcom.example.ExampleImpl
.Dann könnten Sie eine Instanz jeder Implementierung von
Example
(einschließlich einer Instanz vonExampleImpl
) wie folgt erhalten:quelle
Es sollte auch beachtet werden, dass dies natürlich nur alle Unterklassen findet, die auf Ihrem aktuellen Klassenpfad existieren. Vermutlich ist dies in Ordnung für das, was Sie gerade betrachten, und es besteht die Möglichkeit, dass Sie dies in Betracht gezogen haben, aber wenn Sie zu irgendeinem Zeitpunkt eine Nicht-Veröffentlichung veröffentlicht haben
final
in die Wildnis entlassen haben (für unterschiedliche Ebenen von "Wild"), ist dies durchaus machbar jemand anderes hat eine eigene Unterklasse geschrieben, von der Sie nichts wissen.Wenn Sie also zufällig alle Unterklassen sehen möchten, weil Sie eine Änderung vornehmen möchten und sehen möchten, wie sich dies auf das Verhalten der Unterklassen auswirkt, denken Sie an die Unterklassen, die Sie nicht sehen können. Im Idealfall sollten alle Ihre nicht privaten Methoden und die Klasse selbst gut dokumentiert sein. Nehmen Sie Änderungen gemäß dieser Dokumentation vor, ohne die Semantik von Methoden / nicht privaten Feldern zu ändern, und Ihre Änderungen sollten abwärtskompatibel sein, zumindest für jede Unterklasse, die Ihrer Definition der Oberklasse entspricht.
quelle
Der Grund, warum Sie einen Unterschied zwischen Ihrer Implementierung und Eclipse feststellen, besteht darin, dass Sie jedes Mal scannen, während Eclipse (und andere Tools) nur einmal scannen (meistens während des Projektladens) und einen Index erstellen. Wenn Sie das nächste Mal nach den Daten fragen, werden sie nicht erneut gescannt, sondern sehen sich den Index an.
quelle
Ich verwende eine Reflection Lib, die Ihren Klassenpfad nach allen Unterklassen durchsucht: https://github.com/ronmamo/reflections
So würde es gemacht werden:
quelle
Fügen Sie sie einer statischen Zuordnung innerhalb (this.getClass (). GetName ()) des Konstruktors für übergeordnete Klassen hinzu (oder erstellen Sie eine Standardzuordnung), diese wird jedoch zur Laufzeit aktualisiert. Wenn eine verzögerte Initialisierung möglich ist, können Sie diesen Ansatz ausprobieren.
quelle
Sie können die Bibliothek org.reflections verwenden und dann ein Objekt der Reflections-Klasse erstellen. Mit diesem Objekt können Sie eine Liste aller Unterklassen einer bestimmten Klasse abrufen. https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
quelle
Ich musste dies als Testfall tun, um zu sehen, ob dem Code neue Klassen hinzugefügt wurden. Das habe ich getan
quelle