Ich habe ein paar Klassen wie hier gezeigt
%Vor%...
%Vor% Wie kann ich die TrueFalseQuestion
-Klasse ändern, so dass die statische Methode immer ausgeführt wird, so dass ich 1 anstelle von 0 bekomme, wenn ich meine Hauptmethode ausführe? Ich will keine Änderung in der Hauptmethode.
Ich versuche tatsächlich, die Fabrikmuster zu implementieren, in denen sich die Unterklassen bei der Fabrik registrieren, aber ich habe den Code für diese Frage vereinfacht.
Um die Klasse TrueFalseQuestion
mit der Factory zu registrieren, muss ihr statischer Initializer aufgerufen werden. Um den statischen Initialisierer der Klasse TrueFalseQuestion
auszuführen, muss entweder auf die Klasse verwiesen werden oder sie muss durch Reflexion geladen werden, bevor QuestionFactory.map.size()
aufgerufen wird. Wenn Sie die Methode main
unverändert lassen wollen, müssen Sie sie referenzieren oder durch Reflexion im statischen Initiator QuestionFactory
laden. Ich denke nicht, dass dies eine gute Idee ist, aber ich beantworte nur Ihre Frage :) Wenn es Ihnen nichts ausmacht, dass QuestionFactory
alle Klassen kennt, die Question
implementieren, können Sie sie direkt referenzieren oder lade sie durch Reflexion. Etwas wie:
Stellen Sie sicher, dass sich die Deklaration und Konstruktion von map
vor dem static
-Block befindet. Wenn Sie QuestionFactory
nicht über die Implementierungen von Question
wissen wollen, müssen Sie sie in einer Konfigurationsdatei auflisten, die von QuestionFactory
geladen wird. Die einzige andere (möglicherweise wahnsinnige) Art, wie ich es mir vorstellen könnte, wäre, durch den gesamten Klassenpfad nach Klassen zu suchen, die Question
implementieren :) Das könnte besser funktionieren, wenn alle Klassen, die Question
implementiert haben, zur gleiches Paket - HINWEIS: Ich befürworte diese Lösung nicht;)
Der Grund, warum ich das im statischen Initializer QuestionFactory
nicht mache, ist, dass Klassen wie TrueFalseQuestion
ihren eigenen statischen Initialisierer haben, der in QuestionFactory
aufruft, was zu diesem Zeitpunkt ein unvollständig konstruiertes Objekt ist. was nur nach Ärger fragt. Mit einer Konfigurationsdatei, die einfach die Klassen auflistet, die QuestionFactory
konstruieren soll, ist es eine feine Lösung, sie im Konstruktor zu registrieren, aber das würde bedeuten, dass Sie Ihre main
Methode ändern müssen.
Der statische Initialisierer für die Klasse kann nicht ausgeführt werden, wenn die Klasse niemals geladen wird.
Sie müssen also entweder alle korrekten Klassen laden (was schwierig sein wird, da Sie sie nicht alle zur Kompilierzeit kennen) oder die Anforderung für den statischen Initialisierer loswerden.
Eine Möglichkeit, Letzteres zu tun, ist die Verwendung der ServiceLoader
.
Mit dem ServiceLoader
legst du einfach eine Datei in META-INF/services/package.Question
und liesst alle Implementierungen auf. Sie können mehrere solche Dateien haben, eine pro .jar-Datei. Auf diese Weise können Sie einfach weitere Question
Implementierungen separat von Ihrem Hauptprogramm versenden.
In QuestionFactory
kannst du einfach ServiceLodaer.load(Question.class)
verwenden, um ServiceLoader
zu erhalten, was Iterable<Question>
implementiert und wie folgt verwendet werden kann:
Um die statischen Initialisierer ausführen zu können, müssen die Klassen geladen werden. Dazu muss entweder Ihre Hauptklasse (direkt oder indirekt) von den Klassen abhängen oder sie muss direkt oder indirekt dynamisch geladen werden. z.B. mit Class.forName(...)
.
Ich denke, Sie versuchen, Abhängigkeiten zu vermeiden, die in Ihren Quelltext eingebettet sind . Daher sind statische Abhängigkeiten nicht akzeptabel und Aufrufe von Class.forName(...)
mit fest codierten Klassennamen sind ebenfalls nicht akzeptabel.
Damit bleiben Ihnen zwei Alternativen:
Schreiben Sie etwas unordentlichen Code, um über die Ressourcennamen in einem Paket zu iterieren, und verwenden Sie dann Class.forName(...)
, um die Ressourcen zu laden, die Ihren Klassen ähneln. Dieser Ansatz ist schwierig, wenn Sie einen komplizierten Klassenpfad haben, und ist unmöglich, wenn Ihr effektiver Klassenpfad einen URLClassLoader mit einer entfernten URL (zum Beispiel) enthält.
Erstellen Sie eine Datei (z. B. eine Klassenlader-Ressource), die eine Liste der zu ladenden Klassennamen enthält, und schreiben Sie einen einfachen Code, um die Datei zu lesen, und laden Sie sie mit Class.forName(...)
.
Tags und Links java static-members static-initializer