我有一个这样的课程:
class Environment {
somelib.SomeType someOptionalDep = null;
public void doSomething() {
boolean found = false;
try {
Class.forName("somelib.SomeType");
found = true;
} catch (ClassNotFoundException e) {
}
if (found) {
someOptionalDep = new somelib.SomeType();
// ...
}
}
}
somelib
是一个可选包:存在于编译时,但从基础jar中排除。事实上,这段代码没有依赖性(Debian 8上的Oracle Java HotSpot 1.8.0_111),但这真的很安全吗?
注意:我知道,我可以在分离的类中包含可选功能,但在某些情况下这可能会太复杂。
修改
来自Oracle的NoClassDefFoundError
文档:
如果Java虚拟机或
ClassLoader
实例尝试加载类的定义(作为普通方法调用的一部分或作为使用new
表达式创建新实例的一部分),则抛出)并没有找到班级的定义。在编译当前正在执行的类时存在搜索的类定义,但无法再找到该定义。
可悲的是,它没有明确说明声明或加载但未执行的代码。
答案 0 :(得分:1)
SomeType
,并抛出错误。正确的方法是创建一个你也包含在base.jar中的接口,让SomeType实现接口,然后执行:
class Environment {
SomeInterface someOptionalDep = null;
public void doSomething() {
try {
Class c = Class.forName("somelib.SomeType");
someOptionalDep = c.newInstance();
} catch (ClassNotFoundException, InstantiationException, IllegalAccessException e) {
}
}
}
答案 1 :(得分:1)
我相信您的主要问题已在评论
中说明字段声明是否安全且可能不存在类型?
并且安全地你似乎意味着"不会导致NoClassDefFoundError
"。
在回答问题本身之前,我想说你最好不这样做。在代码中使用不存在的类型会使其不必要地复杂化 - 这不是安全的。可选的依赖项最好由一种将业务逻辑与依赖项分开的网关来处理。 Roberto Attias建议的附加界面就是一个例子。您还可以在实现中引入自己的接口:一个委托对可选依赖项的所有调用,另一个不执行任何操作。
从技术上讲,答案是是。
JVM倾向于尽可能懒散地做事 - 特别是类加载。加载类时,JVM只会加载其超类和超接口,而不会加载代码中使用的其他类型。
此外,即使读取和写入具有不存在类型的字段不本身也会引发类加载(和NoClassDefFoundError
)。这是可能的,因为