获取静态初始化块以在 java 中运行而不加载类

发布于 2024-08-27 23:58:09 字数 815 浏览 4 评论 0原文

我有几个类,如下所示

public class TrueFalseQuestion implements Question{
    static{
        QuestionFactory.registerType("TrueFalse", "Question");
    }
    public TrueFalseQuestion(){}
}

...

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }



public class FactoryTester {
    public static void main(String[] args) {
        System.out.println(QuestionFactory.map.size());
        // This prints 0. I want it to print 1
    }
}

如何更改 TrueFalseQuestion 类,以便始终运行静态方法,以便在运行 main 方法时得到 1 而不是 0?我不想对主要方法进行任何更改。

我实际上正在尝试实现子类向工厂注册的工厂模式,但我简化了这个问题的代码。

I have a few classes as shown here

public class TrueFalseQuestion implements Question{
    static{
        QuestionFactory.registerType("TrueFalse", "Question");
    }
    public TrueFalseQuestion(){}
}

...

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }



public class FactoryTester {
    public static void main(String[] args) {
        System.out.println(QuestionFactory.map.size());
        // This prints 0. I want it to print 1
    }
}

How can I change TrueFalseQuestion class so that the static method is always run so that I get 1 instead of 0 when I run my main method? I do not want any change in the main method.

I am actually trying to implement the factory patterns where the subclasses register with the factory but i have simplified the code for this question.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绝影如岚 2024-09-03 23:58:10

如果从未加载该类,则无法执行该类的静态初始值设定项。

因此,您要么需要加载所有正确的类(这将很困难,因为您在编译时并不知道所有这些类),要么摆脱对静态初始值设定项的要求。

执行后者的一种方法是使用 服务加载器

使用 ServiceLoader,您只需将一个文件放入 META-INF/services/package.Question 并列出所有实现即可。您可以拥有多个这样的文件,每个 .jar 文件一个。通过这种方式,您可以轻松地独立于主程序发送额外的 Question 实现。

QuestionFactory 中,您只需使用 ServiceLodaer.load(Question.class) 即可获取实现 IterableServiceLoader ; 可以像这样使用:

for (Question q : ServiceLoader.load(Question.class)) {
    System.out.println(q);
}

The static initializer for the class can't be executed if the class is never loaded.

So you either need to load all the correct classes (which will be hard, since you don't know them all at compile time) or get rid of the requirement for the static initializer.

One way to do the latter is to use the ServiceLoader.

With the ServiceLoader you simply put a file in META-INF/services/package.Question and list all implementations. You can have multiple such files, one per .jar file. This way you can easily ship additional Question implementations separate from your main program.

In the QuestionFactory you can then simply use ServiceLodaer.load(Question.class) to get a ServiceLoader, which implements Iterable<Question> and can be used like this:

for (Question q : ServiceLoader.load(Question.class)) {
    System.out.println(q);
}
森林迷了鹿 2024-09-03 23:58:10

为了运行静态初始化程序,需要加载类。为此,您的“主”类必须(直接或间接)依赖于这些类,或者它必须直接或间接导致它们动态加载;例如使用Class.forName(...)

我认为您正在尝试避免源代码中嵌入的依赖项。因此静态依赖是不可接受的,并且使用硬编码类名调用 Class.forName(...) 也是不可接受的。

这给你留下了两种选择:

  • 编写一些混乱的代码来迭代某些包中的资源名称,然后使用Class.forName(...)加载那些看起来像你的类的资源。如果您有一个复杂的类路径,则这种方法很棘手,并且如果您的有效类路径包含带有远程 URL 的 URLClassLoader(例如),则这种方法是不可能的。

  • 创建一个包含要加载的类名列表的文件(例如类加载器资源),并编写一些简单的代码来读取该文件并使用 Class.forName(...) 来加载每个。

In order to run the static initializers, the classes need to be loaded. For this to happen, either your "main" class must depend (directly or indirectly) on the classes, or it must directly or indirectly cause them to be loaded dynamically; e.g. using Class.forName(...).

I think you are trying to avoid dependencies embedded in your source code. So static dependencies are unacceptable, and calls to Class.forName(...) with hard-coded class names are unacceptable as well.

This leaves you two alternatives:

  • Write some messy code to iterate over the resource names in some package, and then use Class.forName(...) to load those resources that look like your classes. This approach is tricky if you have a complicated classpath, and impossible if your effective classpath includes a URLClassLoader with a remote URL (for example).

  • Create a file (e.g. a classloader resource) containing a list of the classnames that you want loaded, and write some simple code to read the file and use Class.forName(...) to load each one.

攒一口袋星星 2024-09-03 23:58:09

您可以调用:

Class.forName("yourpackage.TrueFalseQuestion");

这将加载该类,而无需您实际接触它,并将执行静态初始化块。

You can call:

Class.forName("yourpackage.TrueFalseQuestion");

this will load the class without you actually touching it, and will execute the static initializer block.

羅雙樹 2024-09-03 23:58:09

要向工厂注册 TrueFalseQuestion 类,需要调用其静态初始值设定项。要执行 TrueFalseQuestion 类的静态初始化程序,需要在调用 QuestionFactory.map.size() 之前引用该类或通过反射加载该类。如果您想保持 main 方法不变,则必须在 QuestionFactory 静态初始化程序中引用它或通过反射加载它。我认为这不是一个好主意,但我只是回答你的问题:)如果你不介意 QuestionFactory 了解所有实现 Question 的类要构造它们,您可以直接引用它们或通过反射加载它们。例如:

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 static {
    this.getClassLoader().loadClass("TrueFalseQuestion");
    this.getClassLoader().loadClass("AnotherTypeOfQuestion"); // etc.
 }

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }

确保 map 的声明和构造位于 static 块之前。如果您不希望 QuestionFactory 了解 Question 的实现,则必须将它们列在由 QuestionFactory 加载的配置文件中。我能想到的唯一其他(可能是疯狂的)方法是在整个类路径中查找实现 Question 的类:)如果所有实现 Question 的类可能会工作得更好 被要求属于同一个包 - 注意:我不认可这个解决方案;)

我不认为在 QuestionFactory 静态初始化程序中执行任何操作的原因是因为类像 TrueFalseQuestion 有自己的静态初始化程序来调用 QuestionFactory,此时它是一个不完全构造的对象,这只是自找麻烦。有一个配置文件,简单地列出您希望 QuestionFactory 知道如何构造的类,然后在其构造函数中注册它们是一个很好的解决方案,但这意味着更改您的 main 方法。

To register the TrueFalseQuestion class with the factory, its static initializer needs to be called. To execute the static initializer of the TrueFalseQuestion class, the class needs to either be referenced or it needs to be loaded by reflection before QuestionFactory.map.size() is called. If you want to leave the main method untouched, you have to reference it or load it by reflection in the QuestionFactory static initializer. I don't think this is a good idea, but I'll just answer your question :) If you don't mind the QuestionFactory knowing about all classes that implement Question to construct them, you can just reference them directly or load them through reflection. Something like:

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 static {
    this.getClassLoader().loadClass("TrueFalseQuestion");
    this.getClassLoader().loadClass("AnotherTypeOfQuestion"); // etc.
 }

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }

Make sure map's declaration and construction is before the static block. If you don't want QuestionFactory to have any knowledge of the implementations of Question, you'll have to list them in a configuration file that gets loaded by QuestionFactory. The only other (possibly insane) way I could think to do it, would be to look through the entire classpath for classes that implement Question :) That might work better if all classes that implemented Question were required to belong to the same package -- NOTE: I am not endorsing this solution ;)

The reason I don't think doing any of this in the QuestionFactory static initializer is because classes like TrueFalseQuestion have their own static initializer that calls into QuestionFactory, which at that point is an incompletely constructed object, which is just asking for trouble. Having a configuration file that simply lists the classes that you want QuestionFactory to know how to construct, then registering them in its constructor is a fine solution, but it would mean changing your main method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文