Ryan Delucchi在这里在第三个评论中向Tom Hawtin提问:
为什么Class.newInstance()是“邪恶的”?
这是对代码示例的回应:
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
那么,为什么它会是邪恶的?
Ryan Delucchi在这里在第三个评论中向Tom Hawtin提问:
为什么Class.newInstance()是“邪恶的”?
这是对代码示例的回应:
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
那么,为什么它会是邪恶的?
换句话说,它可以绕过已检查的异常系统。请注意,此方法会传播由无参构造方法抛出的任何异常,包括已检查异常。使用此方法有效地绕过了编译器将执行的编译时异常检查。
Constructor.newInstance
方法通过在(已检查的)InvocationTargetException
中封装构造函数抛出的任何异常来避免此问题。
Class.newInstance
不会这样做 --- 它会直接抛出已检查异常。缺点是 javac 也不允许你尝试捕获这些异常,因为 Class.newInstance
没有声明抛出它们。 - C. K. Young另一个原因:
现代IDE允许您查找类的用法-这在重构期间很有帮助,如果您和您的IDE知道使用要更改的类的代码。
当您不显式使用构造函数,而是使用Class.newInstance()时,您可能会冒着在重构期间无法找到该使用情况的风险,并且在编译时不会表现出此问题。
Constructor::newInstance
,因为自从 Java 9 开始,Class::newInstance
已经被弃用了。static class Foo {
public Foo() throws IOException {
throw new IOException();
}
}
当您尝试通过反射创建它的实例时。首先使用 Class::newInstance
:
Class<Foo> clazz = ...
try {
clazz.newInstance();
} catch (InstantiationException e) {
// handle 1
} catch (IllegalAccessException e) {
// handle 2
}
调用此方法将导致引发 IOException
- 问题在于您的代码未处理它,既不会被handle 1
也不会被handle 2
捕获。Constructor
执行,则: Constructor<Foo> constructor = null;
try {
constructor = clazz.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
Foo foo = constructor.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
System.out.println("handle 3 called");
e.printStackTrace();
}
那么处理3将被调用,因此您将处理它。
实际上,Class::newInstance
绕过了异常处理 - 这确实不是您想要的。