Ryan Delucchi在评论 #3 中对Tom Hawtin的回答提问:
为什么 Class.newInstance() “邪恶”?
这是对代码示例的响应:
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
那么,为什么它是邪恶的?
Ryan Delucchi在评论 #3 中对Tom Hawtin的回答提问:
为什么 Class.newInstance() “邪恶”?
这是对代码示例的响应:
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
那么,为什么它是邪恶的?
Java API 文档解释了原因(http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance()):
请注意,此方法传播由 nullary 构造函数引发的任何异常,包括已检查的异常。使用此方法可以有效地绕过编译器执行的编译时异常检查。该
Constructor.newInstance
方法通过将构造函数抛出的任何异常包装在 (checked) 中来避免此问题InvocationTargetException
。
换句话说,它可以打败检查异常系统。
还有一个原因:
现代 IDE 允许您找到类的用法 - 如果您和您的 IDE 知道哪些代码正在使用您计划更改的类,那么它在重构期间会有所帮助。
当您不显式使用构造函数,而是使用 Class.newInstance() 时,您可能会在重构期间找不到该用法,并且此问题在您编译时不会表现出来。
我不知道为什么没有人为此提供一个简单的基于示例的解释,Constructor::newInstance
例如,因为finally Class::newInstance
自 java-9 以来已被弃用。
假设你有这个非常简单的类(不管它坏了):
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
不会捕获它。
相比之下,通过 a 进行操作时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
绕过了异常处理——你真的不想要。