我不得不承认这是一个表面问题,但是我没有找到更直接的解决方案这一事实使我认为我可能缺少了一些东西。
问题是,我的类(比如Foo)有一个非常重要的static
块,它在Foo.class
中通过builder方法注册了自己(Map
),如下所示:
// somewhere in the class
static {
Bar.registerBuilder(Foo.class, Foo::build);
}
这使得有可能从Foo
类中获得Bar
构建器,有点像这样:
// somewhere in a method
Foo foo = Bar.getBuilder(Foo.class).apply("Hello World");
(如果构建器采用String
参数)。但是,仅在Foo
类已经初始化的情况下,上层代码示例才有效。否则,这意味着static
的{{1}}块尚未执行,并且构建器现在尚未在Foo
中注册,这导致Bar
返回{{ 1}}和getBuilder()
抛出null
。
感谢互联网(主要是StackOverflow),我发现可以强制使用apply()
。但是真正令我困惑的是,该方法需要一个NullPointerException
(因此会抛出选中的Class.forName(String)
),我还没有找到直接通过String
实例加载和初始化类的方法。 。我本来希望像这样
ClassNotFoundException
相反,我必须这样做:
java.lang.Class
我想知道我是否完全丢失了什么,或者是否有更干净的方法通过Class<Foo> clazz = Foo.class;
clazz.load(); // does not exist
表示来加载和初始化类。
感谢您的帮助,谢谢!
编辑1:正如@Boris the Spider在评论中指出的那样,Class<Foo> clazz = Foo.class;
try {
Class.forName(clazz.getName());
} catch (ClassNotFoundException) {
// handle an exception that is actually unreachable
}
应该已经加载并初始化了该类,但是(至少在我的情况下)并没有加载,这就是我什至遇到的原因这个问题。
编辑2:使用“复杂”方式通过java.lang.Class
加载类(如代码示例中所示)实际上可以解决我所认为的问题。只是我想尽可能使用更简洁的方法。
使用:
答案 0 :(得分:1)
如果您已经在引用该类,则最好将该静态代码移动到普通的静态工厂方法中。为什么在只运行该方法时为什么要使用反射或尝试引用某个类以使某些代码运行?
public static BuilderFunction createBuilder() {
return Foo::build;
}
只需在Bar
的静态块中调用它即可:
registerBuilder(Foo.class, Foo.createBuilder());
如果您需要更多动态的东西,则可以使用服务加载器,尤其是在Java 9+中,因为它们现在更好用了:
provides my.BuilderProvder with something.FooProvider;
然后将它们全部加载到Bar中:
ServiceLoader<BuilderProvder> loader = ServiceLoader.load(BuilderProvder.class);
loader.stream()
.forEach(provider -> registerBuilder(provider));
现在,即使您未开发的不同模块也可以提供自己的构建器,并且您不需要进行任何手动的类加载(并且只有在实际使用类(例如使用某些方法或字段)的情况下,才保证类初始化-注意该常量在编译时内联,因此不计在内)。
您还可以使用一些骇人的反射库(例如ClassGraph或Reflections)来获取给定类型/具有给定注释的所有类,然后加载它们并在它们上调用一些init方法,就像我在第一个建议的解决方案中使用{{1} }。这是spring中注册了多少个组件,可以使用java批注预处理完成类似的操作,以便在编译时找到此类并仅保存名称。但是,如果可能的话,我建议您坚持使用现有的内置解决方案,例如服务加载程序。