【问题标题】:Why is Class.newInstance() "evil"?为什么 Class.newInstance() 是“邪恶的”?
【发布时间】:2010-09-16 18:09:05
【问题描述】:

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

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

这是对代码示例的响应:

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

那么,为什么它是邪恶的?

【问题讨论】:

  • 实际上看到了这个问题的答案:人们可以这样说反射的各种用途......不仅仅是 Class.newInstance()。所以这实际上是一个普遍的观察,即“反射击败了编译时检查”......这通常是反射的重点。
  • 现在的孩子们,哦,是的,他们到处乱写“邪恶”这个词,但他们甚至从未见过 COBOL 或 FORTRAN 程序!你想“邪恶”看看 20 年前的 FORTRAN 程序,它是由具有模拟背景且没有 CS 影响的修补程序从一个项目传递到另一个项目的!现在是“邪恶!”

标签: java constructor runtime instantiation


【解决方案1】:

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

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

换句话说,它可以击败检查异常系统。

【讨论】:

  • 这就是一般反射的本质......完全不特定于 Constructor.newInstance()。
  • @Ryan:那不是真的;所有其他基于反射的调用方法都会抛出一个名为 InvocationTargetException 的已检查异常,该异常包装了被调用方法抛出的任何 throwable。 Class.newInstance 不会那样做——它会直接抛出检查异常。缺点是 javac 也不会让你尝试捕获这些异常,因为 Class.newInstance 没有声明抛出它们。
【解决方案2】:

还有一个原因:

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

当您不显式使用构造函数,而是使用 Class.newInstance() 时,您可能会在重构期间找不到该用法,并且此问题在您编译时不会表现出来。

【讨论】:

  • 也是使用反射的一般问题。
【解决方案3】:

我不知道为什么没有人为此提供一个简单的基于示例的解释,例如,与 Constructor::newInstance 相比,因为 终于 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 都不会捕获它。

相比之下,通过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 绕过了异常处理——这是你不想要的。

【讨论】:

    猜你喜欢
    • 2010-11-22
    • 2011-02-04
    • 2012-10-10
    • 2010-10-01
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-18
    相关资源
    最近更新 更多