java - 使用 java.lang.Class 初始化(加载)一个 java 类实例
问题描述
我不得不承认这更像是一个表面问题,但我还没有找到更直接的解决方案这一事实让我觉得我可能遗漏了一些东西。
问题是,我的类(比方说 Foo)有一个非常重要的static
块,它Foo.class
使用 a 中的构建器方法注册自己()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
块Foo
尚未执行,并且构建器现在尚未注册Bar
,这将导致getBuilder()
返回null
并apply()
抛出NullPointerException
.
感谢互联网(主要是 StackOverflow),我发现您可以强制使用Class.forName(String)
. 但真正让我感到困惑的是,这个方法需要 a String
(因此抛出了 checked ClassNotFoundException
),而我还没有找到直接通过java.lang.Class
实例加载和初始化类的方法。我本来期望像
Class<Foo> clazz = Foo.class;
clazz.load(); // does not exist
相反,我必须这样做:
Class<Foo> clazz = Foo.class;
try {
Class.forName(clazz.getName());
} catch (ClassNotFoundException) {
// handle an exception that is actually unreachable
}
我想知道我是否完全遗漏了某些东西,或者如果没有,是否有一种更简洁的方法可以通过java.lang.Class
表示加载和初始化一个类。
任何帮助表示赞赏,谢谢!
编辑1:正如@Boris the Spider 在评论中指出的那样,Foo.class
可能应该已经加载并初始化了该类,但它没有(至少在我的情况下),这就是我什至遇到这个问题的原因。
编辑2:使用“复杂”的方式来加载类Class.forName()
(如代码示例中所示)实际上解决了我认为的问题。只是如果可能的话,我想使用一种更清洁的方式。
使用:
- Java 11 (openjdk 11.0.2)
- IntelliJ IDEA Ultimate (2019.3)
- Maven (3.6.3)
解决方案
如果您已经在引用该类,最好将该静态代码移到普通的静态工厂方法中。既然可以运行该方法,为什么还要使用反射或尝试引用某个类只是为了使某些代码运行?
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));
现在,即使不是您开发的不同模块也可以提供自己的构建器,并且您不需要进行任何手动类加载(并且只有在实际使用类时才保证发生类初始化,例如使用某些方法或字段 - 请注意,常量是在编译时内联,因此它们不计算在内)。
您还可以使用一些 hacky 反射库,如 ClassGraph 或 Reflections 来获取给定类型/具有给定注释的所有类,然后加载它们并在它们上调用一些 init 方法,就像我第一个提出的解决方案一样createBuilder
。这是spring内部注册了多少组件,类似的事情可以用java注解预处理在编译时找到这个类并保存名称。但如果可能的话,我建议坚持使用现有的内置解决方案,如服务加载程序。
推荐阅读
- amazon-web-services - 如何将 aws iam list-account-aliases 的值作为变量?
- python - 无法连接到 Python 发出的 QML 中的信号
- vue.js - Vuetify 下载以前的版本 1.5
- deployment - 如何获取 AppVeyor webhook 通知的部署编号?
- python - 矩阵中第一行的元素使用每行中的一个元素进行操作,迭代行的所有元素
- reactjs - 如何不在特定页面上显示导航栏?
- python - 如何确保从电子表格中获取的任何数据都没有 xml 内容?
- mongodb - Mongodb $unwind 删除对象。如果 $lookup 没有参考
- sas - 根据另一个变量的值创建变量
- php - 使用 MYSQL 进行 PHP 密码验证