为什么Class.newInstance()“邪恶”?

时间:2008-10-12 10:40:12

标签: java constructor runtime instantiation

Ryan Delucchi在评论#3中here询问Tom Hawtin的回答:

  

为什么Class.newInstance()“邪恶”?

这是对代码示例的回应:

// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();

所以,为什么它是邪恶的?

3 个答案:

答案 0 :(得分:80)

Java API文档解释了为什么(http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance()):

  

请注意,此方法传播由nullary构造函数抛出的任何异常,包括已检查的异常。使用此方法可以有效地绕过编译时异常检查,否则将由编译器执行。 Constructor.newInstance方法通过将构造函数抛出的任何异常包装在(已检查的)InvocationTargetException中来避免此问题。

换句话说,它可以击败已检查的例外系统。

答案 1 :(得分:19)

还有一个原因:

现代IDE允许您查找类使用 - 如果您和您的IDE知道哪些代码正在使用您计划更改的类,那么它在重构期间会有所帮助。

如果您没有明确使用构造函数,而是使用Class.newInstance(),则可能无法在重构期间找到该用法,并且在编译时此问题不会自行显现。

答案 2 :(得分:7)

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 1handle 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绕过了您真正不想要的异常处理。