Why is Class.newInstance() "evil"?
Asked Answered
B

3

100

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?

Before answered 12/10, 2008 at 10:40 Comment(3)
Actually seeing the answers to this question: one could say this about a variety of uses of reflection ... not just Class.newInstance(). So this is really a general observation that "reflection defeats compile-time checking" ... which is often the point of reflection.Minni
Kids these days, Oh yea they throw around the word "EVIL" but they have never even SEEN a COBOL or FORTRAN program! You want "EVIL" take a look at 20 year old FORTRAN program that was passed from project to project by tinker's with a simulation background and no CS influence what so ever! Now that "EVIL!"Mockup
See also https://mcmap.net/q/57734/-how-should-i-select-which-concrete-implementation-should-be-instantiated-based-on-the-user-choice/3888450Cowper
O
86

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.

Ovid answered 12/10, 2008 at 10:43 Comment(2)
This is the very nature of reflection in general ... not at all specific to Constructor.newInstance().Minni
@Ryan: That is not true; all the other reflection-based invocation methods throw a checked exception called InvocationTargetException that wraps any throwable thrown by the invoked method. Class.newInstance won't do that---it will throw the checked exception directly. The downside is that javac won't let you try to catch those exceptions either, because Class.newInstance isn't declared to throw them.Ovid
S
22

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.

Sport answered 13/10, 2008 at 3:46 Comment(1)
Also a general gotcha of using reflection.Minni
D
17

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.

Dance answered 26/10, 2018 at 18:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.