Why Is Class.Newinstance() "Evil"

Why is Class.newInstance() evil?

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.

Java 9 replace Class.newInstance

These two calls invoke the same constructor, the zero-argument constructor:

  1. klass.newInstance()
  2. klass.getDeclaredConstructor().newInstance()

Both perform the same run-time check to verify the caller's access, if the constructor is not public. The only difference is that #2 wraps any checked exceptions instead of directly throwing. Otherwise they are identical and you can replace one with the other.

But this is different:


  1. klass.getConstructor().newInstance()

because it can only return a public constructor. It throws a NoSuchMethodException if the constructor is not public.

So you can't change it to getConstructor() unless you know the constructor is public.

T::class.java.newInstance() deprecated

Actually this comes from Java itself. The appropriate replacement is:

T::class.java.getDeclaredConstructor().newInstance()

You can also check the Class.newInstance()-Javadoc which states that too.

`Class? extends BaseTab.newInstance()` throws java.lang.StackOverflowError with Reflections

As you wrote in your comment, the first code snippet appears in a constructor. That snippet creates new instances, which causes that consturctor to be called again. This results in an infinite recursion that ends in stack overflow error.

Why obtain a constructor object instead of directly creating an instance?

Take a look at this similar question. This link also gives some more examples and a more detailed explanation.

To sum up the arguments (from the second link):

Class.newInstance() can only invoke the zero-argument constructor,
while Constructor.newInstance() may invoke any constructor, regardless
of the number of parameters.

Class.newInstance() throws any exception thrown by the constructor,
regardless of whether it is checked or unchecked.

Class.newInstance() requires that the constructor be visible;
Constructor.newInstance() may invoke private constructors under certain
circumstances.

Sorry for editing my post to add more quotes, but this explains it better than I could.
The Javadoc for Class.newInstance() explains it like this:

Creates a new instance of the class represented by this Class object. The class is instantiated as if by a new expression with an empty argument list. The class is initialized if it has not already been initialized.

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.

Why do I have to cast the return of Constructor.newInstance?

Generics are figments of the compiler's imagination: The runtime (java.exe) doesn't know what it means, doesn't care, and mostly, doesn't even have this information ('erasure'). Yes, public signatures do carry generics info, but it's literally a comment as far as the runtime is concerned.

Thus, what use is it, really?

Well, it links things. It makes it possible to tell the compiler: So, I have this method, and it takes 2 parameters. These 2 parameters can be whatever you please, but, they must be the same type. If they aren't, the runtime isn't going to core dump or anything, just.. that'd mean the software is buggy. So, if you see code that fails this rule, please mark it with an error so I know I need to fix it.

That's all generics does. But that's rather useful, fortunately!

If you use generics and they show up in only one place, that's basically useless, or hacky. You've done that here: T shows up only once, as the return type of this method, and nowhere else. That's, virtually always, an indication that the author of the code does not understand what java's generics do.

In this case, what you've effectively done is turn this into a voodoo magic any-method - this method will adapt its return type to be whatever the caller wishes it to be. Given class Parent and class Joe extends Parent and class Jane extends Parent, then this will work:

Joe c = getConfig();

How can getConfig() know that its return type is in fact Joe? It doesn't. It adapts to be what you want it to be.

Which is a bad thing - because if you look at the code, that's not actually how it works here:

class ConfigTool {
private final Class<? extends Parent> c;

public ConfigTool(Class<? extends Parent> c) {
this.c = c;
}

public <T extends ParentClass> T getConfig() {
return (T) c.newInstance();
}
}

and:

ConfigTool tool = new ConfigTool(Jane.class);
Joe joe = tool.getConfig();

will just compile. And, of course, explode at runtime: the getConfig() method ends up making an instance of Jane, which cannot be assigned to a variable of type Joe, which suggests that there is no easy 'this is how to fix your code' answer, because what you've written is meaningless / impossible.

What you presumably intend is for one of two things:

Either:

public ParentClass getConfig() {
return c.newInstance();
}

Sure, maybe c is the Class instance representing Joe, but it could also be Jane. The only promise your API can make is that, whatever that Class instance may be, whatever instance it makes will be assignable to a variable of type Parent. After all, Parent p = new Joe(); is valid, as is Parent p = new Jane();.

There's no need to involve generics. Generics is one 'meta' level higher: It's turning the type system itself into a programming language, and you don't need to do that, here.

The other alternative that you may want is to link the return type of your getConfig() method to the particular type of the Class instance for this object:

class ConfigTool<T extends Parent> {
private final Class<T> c;

public ConfigTool(Class<T> c) {
this.c = c;
}

public T getConfig() {
return c.newInstance();
}
}

Note how in this code there is absolutely no need for that cast, the above compiles as written. The generics are also useful, as T shows up in 2 places: Both the Class<T> parameter in the constructor, as well as the T return type of the getConfig method (its appearance in class Parent<T> doesn't count, that isn't using it, that's defining its existence).

reflection newinstance constructor java wrong number of arguments

You're calling the first of the two constructors without argument. But how can you be sure that the first constructor in the array is the one without argument, and not the one expecting a String?

You can't, because the javadoc says:

The elements in the array returned are not sorted and are not in any particular order.

If you want a reference to the no-argument constructor of the class, you should call

cl.getDeclaredConstructor();

If you want a reference to the constructor taking a STring as argument, you should call

cl.getDeclaredConstructor(String.class);


Related Topics



Leave a reply



Submit