lade den statischen Initialisierungsblock in einem Java, ohne die Klasse zu laden

8

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.

    
randomThought 06.04.2010, 05:46
quelle

4 Antworten

5

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:

%Vor%

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.

    
Rob Heiser 06.04.2010, 06:33
quelle
6

Sie können anrufen:

%Vor%

Dies lädt die Klasse, ohne dass Sie sie tatsächlich berühren, und führt den statischen Initialisierungsblock aus.

    
Bozho 06.04.2010 05:50
quelle
3

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:

%Vor%     
Joachim Sauer 06.04.2010 06:50
quelle
2

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(...) .

Stephen C 06.04.2010 06:56
quelle