Ich möchte so etwas machen:
List<Animal> animals = new ArrayList<Animal>();
for( Class c: list_of_all_classes_available_to_my_app() )
if (c is Animal)
animals.add( new c() );
Ich möchte mir also alle Klassen im Universum meiner Anwendung ansehen. Wenn ich eine finde, die von Animal abstammt, möchte ich ein neues Objekt dieses Typs erstellen und zur Liste hinzufügen. Auf diese Weise kann ich Funktionen hinzufügen, ohne eine Liste von Dingen aktualisieren zu müssen. Ich kann Folgendes vermeiden:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
Mit dem obigen Ansatz kann ich einfach eine neue Klasse erstellen, die Animal erweitert, und sie wird automatisch aufgenommen.
UPDATE: 16.10.2008 9:00 Uhr pazifische Standardzeit:
Diese Frage hat viele gute Antworten hervorgebracht - danke. Aus den Antworten und meinen Recherchen habe ich herausgefunden, dass das, was ich wirklich tun möchte, unter Java einfach nicht möglich ist. Es gibt Ansätze wie den ServiceLoader-Mechanismus von ddimitrov, die funktionieren können - aber sie sind sehr schwer für das, was ich will, und ich glaube, ich verschiebe das Problem einfach von Java-Code in eine externe Konfigurationsdatei. Update 10.05.19 (11 Jahre später!) Es gibt jetzt mehrere Bibliotheken, die laut @ IvanNiks Antwort helfen können . Org.reflections sieht gut aus. Auch ClassGraph aus der Antwort von @Luke Hutchison sieht interessant aus. Es gibt auch mehrere weitere Möglichkeiten in den Antworten.
Eine andere Möglichkeit, anzugeben, was ich möchte: Eine statische Funktion in meiner Animal-Klasse findet und instanziiert alle Klassen, die von Animal erben - ohne weitere Konfiguration / Codierung. Wenn ich konfigurieren muss, kann ich sie genauso gut in der Animal-Klasse instanziieren. Ich verstehe das, weil ein Java-Programm nur ein loser Verbund von .class-Dateien ist, so ist es eben.
Interessanterweise scheint dies in C # ziemlich trivial zu sein.
quelle
Antworten:
Ich benutze org.reflections :
Ein anderes Beispiel:
quelle
animals.add( new c() );
des Codes), weil währendClass.newInstance()
vorhanden ist , haben Sie keine Garantie , dass die spezifische Klasse einen parameterlosen Konstruktor hat (C #, dass in einem allgemeinen erfordern erlaubt)Die Java-Methode, um das zu tun, was Sie möchten, ist die Verwendung des ServiceLoader- Mechanismus.
Viele Benutzer rollen auch ihre eigenen, indem sie eine Datei an einem bekannten Klassenpfad (z. B. /META-INF/services/myplugin.properties) haben und dann mit ClassLoader.getResources () alle Dateien mit diesem Namen aus allen Jars auflisten . Auf diese Weise kann jedes Glas seine eigenen Anbieter exportieren und Sie können sie durch Reflektion mit Class.forName () instanziieren.
quelle
Denken Sie darüber aus einer aspektorientierten Perspektive nach. Was Sie wirklich tun möchten, ist, alle Klassen zur Laufzeit zu kennen, die die Animal-Klasse erweitert haben. (Ich denke, das ist eine etwas genauere Beschreibung Ihres Problems als Ihr Titel; ansonsten glaube ich nicht, dass Sie eine Laufzeitfrage haben.)
Ich denke, Sie möchten einen Konstruktor Ihrer Basisklasse (Animal) erstellen, der Ihrem statischen Array (ich bevorzuge ArrayLists selbst, aber jedem eigenen) den Typ der aktuellen Klasse hinzufügt, die instanziiert wird.
Also ungefähr;
Natürlich benötigen Sie einen statischen Konstruktor für Animal, um instanziierteDerivedClass zu initialisieren ... Ich denke, dies wird das tun, was Sie wahrscheinlich wollen. Beachten Sie, dass dies vom Ausführungspfad abhängig ist. Wenn Sie eine Hundeklasse haben, die von einem Tier abgeleitet ist, das niemals aufgerufen wird, haben Sie sie nicht in Ihrer Tierklassenliste.
quelle
Leider ist dies nicht ganz möglich, da der ClassLoader Ihnen nicht sagt, welche Klassen verfügbar sind. Sie können jedoch ziemlich nahe kommen, wenn Sie so etwas tun:
Bearbeiten: johnstok ist richtig (in den Kommentaren), dass dies nur für eigenständige Java-Anwendungen funktioniert und nicht unter einem Anwendungsserver.
quelle
URL[]
aber nicht immer, so dass dies möglicherweise nicht möglich ist) aus der ClassLoader-Hierarchie abfragen. Oft müssen Sie jedoch über eine Berechtigung verfügen, da in der Regel eineSecurityManager
JVM geladen wird.Sie können ResolverUtil ( Rohquelle ) aus dem Stripes Framework verwenden,
wenn Sie etwas Einfaches und Schnelles benötigen, ohne vorhandenen Code umzugestalten.
Hier ist ein einfaches Beispiel, in dem keine der Klassen geladen wurde:
Dies funktioniert auch auf einem Anwendungsserver, da er dort entwickelt wurde;)
Der Code macht im Wesentlichen Folgendes:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
quelle
Der robusteste Mechanismus zum Auflisten aller Unterklassen einer bestimmten Klasse ist derzeit ClassGraph , da er die größtmögliche Anzahl von Klassenpfadspezifikationsmechanismen verarbeitet , einschließlich des neuen JPMS-Modulsystems. (Ich bin der Autor.)
quelle
Java lädt Klassen dynamisch, sodass Ihr Klassenuniversum nur diejenigen sind, die bereits geladen (und noch nicht entladen) wurden. Vielleicht können Sie mit einem benutzerdefinierten Klassenladeprogramm etwas tun, das die Supertypen jeder geladenen Klasse überprüft. Ich glaube nicht, dass es eine API gibt, um die geladenen Klassen abzufragen.
quelle
benutze das
Bearbeiten:
quelle
Vielen Dank an alle, die diese Frage beantwortet haben.
Es scheint, dass dies in der Tat eine harte Nuss ist. Am Ende gab ich es auf und erstellte ein statisches Array und einen Getter in meiner Basisklasse.
Es scheint, dass Java nicht so auf Selbsterkennbarkeit eingerichtet ist wie C #. Ich nehme an, das Problem ist, dass die Laufzeit nichts über eine Klasse weiß, bis eine Java-App nur eine Sammlung von .class-Dateien ist, die sich irgendwo in einem Verzeichnis / einer JAR-Datei befinden. Zu diesem Zeitpunkt lädt der Loader es - ich versuche es zu entdecken, bevor ich darauf verweise, was nicht möglich ist, ohne zum Dateisystem zu gehen und nachzuschauen.
Ich mag immer Code, der sich selbst entdecken kann, anstatt dass ich ihn über sich selbst erzählen muss, aber leider funktioniert das auch.
Danke noch einmal!
quelle
Mit OpenPojo können Sie Folgendes tun:
quelle
Dies ist ein schwieriges Problem, und Sie müssen diese Informationen mithilfe einer statischen Analyse ermitteln, die zur Laufzeit nicht einfach verfügbar ist. Holen Sie sich im Grunde den Klassenpfad Ihrer App und durchsuchen Sie die verfügbaren Klassen und lesen Sie die Bytecode-Informationen einer Klasse, von welcher Klasse sie erbt. Beachten Sie, dass ein Klassenhund möglicherweise nicht direkt von Animal erbt, sondern möglicherweise von Pet, das wiederum von Animal erbt. Daher müssen Sie diese Hierarchie im Auge behalten.
quelle
Eine Möglichkeit besteht darin, die Klassen dazu zu bringen, statische Initialisierer zu verwenden ... Ich glaube nicht, dass diese vererbt werden (es funktioniert nicht, wenn sie es sind):
Sie müssen diesen Code allen beteiligten Klassen hinzufügen. Aber es vermeidet, irgendwo eine große hässliche Schleife zu haben, die jede Klasse auf der Suche nach Kindern von Animal testet.
quelle
Ich habe dieses Problem ziemlich elegant mithilfe von Annotationen auf Paketebene gelöst und diese Annotation dann als Argument für eine Liste von Klassen verwendet.
Suchen Sie nach Java-Klassen, die eine Schnittstelle implementieren
Implementierungen müssen lediglich eine package-info.java erstellen und die magische Annotation mit der Liste der Klassen einfügen, die sie unterstützen möchten.
quelle