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

发布于 2024-07-07 21:17:33 字数 569 浏览 10 评论 0原文

Ryan Delucchi询问这里在评论#3中给Tom霍廷的回答:

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

这是对代码示例的回应:

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

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

Ryan Delucchi asked here in comment #3 to Tom Hawtin's answer:

why is Class.newInstance() "evil"?

this in response to the code sample:

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

so, why is it Evil?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

御弟哥哥 2024-07-14 21:17:33

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

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

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

The Java API documentation explains why (http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance()):

Note that this method propagates any exception thrown by the nullary constructor, including a checked exception. Use of this method effectively bypasses the compile-time exception checking that would otherwise be performed by the compiler. The Constructor.newInstance method avoids this problem by wrapping any exception thrown by the constructor in a (checked) InvocationTargetException.

In other words, it can defeat the checked exceptions system.

装纯掩盖桑 2024-07-14 21:17:33

还有一个原因:

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

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

One more reason:

Modern IDEs allow you to find class usages - it helps during refactoring, if you and your IDE know what code is using class that you plan to change.

When you don't do an explicit usage of the constructor, but use Class.newInstance() instead, you risk not to find that usage during refactoring and this problem will not manifest itself when you compile.

鹤仙姿 2024-07-14 21:17:33

我不知道为什么没有人对此提供一个简单的基于示例的解释,例如与 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 也无法捕获它。

相反,当通过构造函数执行此操作时:

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

I don't know why no one provided a simple example based explanation to this, as compared to Constructor::newInstance for example, since finally Class::newInstance was deprecated since java-9.

Suppose you have this very simple class (does not matter that it is broken):

static class Foo {
    public Foo() throws IOException {
        throw new IOException();
    }
}

And you try to create an instance of it via reflection. First Class::newInstance:

    Class<Foo> clazz = ...

    try {
        clazz.newInstance();
    } catch (InstantiationException e) {
        // handle 1
    } catch (IllegalAccessException e) {
        // handle 2
    }

Calling this will result in a IOException being thrown - problem is that your code does not handle it, neither handle 1 nor handle 2 will catch it.

In contrast when doing it via 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();
    }

that handle 3 will be called, thus you will handle it.

Effectively, Class::newInstance bypasses the exception handling - which you really don't want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文