Currently, I have a situation where I am trying to instantiate an object of a class, with the class given as a parameter, and have the object's properties initialized with default values (e.g. null). THe problem is, my class does not have any constructors and I am not able to add one for various reasons. The possible classes are also not connected by a superclass, they are all direct subclasses of Object. The classes I am trying to instantiate look like the following:
public class MyClass {
public String prop1;
public int prop2;
public int prop3;
}
That means the classes contain only properties and no methods, and as such no explicit constuctors. In cases where I know the class beforehand, I can of course just do this:
public MyClass create() {
return new MyClass();
}
In the above case, the compiler generates me a default constructor for MyClass and initiates all properties with their default values. As pointed out in the comments, this means I technically have a constructor, but one that I am only able to access using the "new" keyword and by knowing the class beforehand. Now, what I am trying to do (basically) is the following:
public someClass create(Class<?> someClass) {
return new someClass();
}
Of course, the 'new' keyword won't work in this scenario, so I have tried out many alternatives, none of which have worked so far:
- Using
someClass.getConstructor().newInstance()
orsomeClass.getDeclaredConstructor().newInstance()
. Code compiled but I got the expected "NoSuchMethodException" as it could not find an <init>-method or constructor for someClass - Casting from Object like
Object newObject = someClass.cast(new Object())
. As expected, this crashes as well, as you can't cast from a superclass to a subclass
My only real alternative right now, as the range of possible classes is not too large, is to conjure up a massive switch-case using "instanceof"s for every class and then just using the "new" keyword in each branch. But as you can imagine, I am not too fond of that.
So am I trying to do the impossible or is there a way to do this?
SOLUTION: Turns out the first method was perfectly valid, it was only not working because some of my classes were Arrays, which I did not realize. If I take the actual classes of the objects in the arrays, it works. Sorry for the confusion, it was my mistake all along!
CodePudding user response:
Method Class::getConstructor
returns public constructor, so it may not detect a non-public no-args constructor:
Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object. The parameterTypes parameter is an array of Class objects that identify the constructor's formal parameter types, in declared order. If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.
The constructor to reflect is the public constructor of the class represented by this Class object whose formal parameter types match those specified by parameterTypes.
So, method create(Class<?> someClass)
should be defined and implemented to call Class::newInstance
, however, as it is deprecated since Java 9 it should be replaced by someClass.getDeclaredConstructor().newInstance()
.
Also, checked exceptions may be caught and wrapped into a RuntimeException
public static <T> T create(Class<T> someClass) {
try {
return someClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
throw new RuntimeException("Failed to create instance of class " someClass.getName(), ex);
}
}
Test:
MyClass mc = create(MyClass.class);
System.out.println(mc); // overridden toString
Output
MyClass :: prop1 = null; prop2 = 0; prop3 = 0