I'd like to create a registry for classes which are subclasses of a super class. The classes are stored in a map which acts as registry. A class is picked from the registry depending on a key and an instance of that class will be created via reflection.
I'd like to instantiate a class depending on a constructor (with 1 parameter) of the super class. It works only if I declare the constructor in the subclasses as well.
Is there a way to instantiate the class using a constructor of a super class? Is there a way to make that code type-safe?
Example code:
public class ReflectionTest {
/**
* Base class with no-args constructor and another constructor with 1 parameter
*/
public static class BaseClass {
Object object;
public BaseClass() {
System.out.println("Constructor with no args");
}
public BaseClass( Object object) {
this.object = object;
System.out.println("Constructor with parameter= " + object);
}
public String toString() {
return "Object = " + object;
}
}
/**
* Subclass with 1 parameter constructor
*/
public static class SubClass1 extends BaseClass {
public SubClass1( Object object) {
super(object);
}
}
/**
* Subclass with no-args constructor
*/
public static class SubClass2 extends BaseClass {
}
public static void main(String[] args) {
// registry for classes
Map<Integer,Class<?>> registry = new HashMap<>();
registry.put(0, SubClass1.class);
registry.put(1, SubClass2.class);
// iterate through classes and create instances
for( Integer key: registry.keySet()) {
// get class from registry
Class<?> clazz = registry.get(key);
try {
// get constructor with parameter
Constructor constructor = clazz.getDeclaredConstructor( Object.class);
// instantiate class
BaseClass instance = (BaseClass) constructor.newInstance(key);
// logging
System.out.println("Instance for key " + key + ", " + instance);
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
System.exit(0);
}
}
The example gives the following console output:
Constructor with parameter= 0
Instance for key 0, Object = 0
java.lang.NoSuchMethodException: swing.table.ReflectionTest$SubClass2.<init>(java.lang.Object)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getConstructor(Class.java:1825)
at swing.table.ReflectionTest.main(ReflectionTest.java:63)
To explicitly call the superclass constructor from the subclass constructor, we use super() . It's a special form of the super keyword. super() can be used only inside the subclass constructor and must be the first statement.
No, we cannot call subclass constructor from superclass constructor.
(*2) A subclass constructor always calls the constructor of its superclass, and so on up to the Object constructor, to initialize all the aspects of the instance that the subclass inherits from its superclass.
Use __init()__ on super() inside the constructor of the subclass to invoke the constructor of the superclass in Python. In inheritance invoking the super constructor of a subclass invokes the constructor of its superclass.
Therefore, without making assumptions about the subclass ctor, you cannot write the code that you want.
So what can you do? Use an Abstract Factory pattern.
We can create an interface Factory
:
@FunctionalInterface
public interface SuperclassFactory {
Superclass newInstance(Object o);
}
You could create more than one method on the Factory
, but that would make it less neat for lambdas.
Now you have a Map<Integer, SuperclassFactory>
, and populate it:
Map<Integer,SuperclassFactory> registry = new HashMap<>();
registry.put(0, SubClass1::new);
registry.put(1, SubClass2::new);
So, in order to use this Map
you simply do:
for(final Map.Entry<Integer,SuperclassFactory> e: registry.entrySet()) {
//...
final BaseClass instance = e.getValue().newInstance(e.getKey());
//...
}
If your subclass does not have the appropriate ctor, this code will not compile as there will be no ctor reference that can be used. This is Good Thing (TM). In order to compile with the current Subclass2
you would need to use:
registry.put(1, obj -> new SubClass2());
So now we have:
N.B. loop through a Map
's entrySet()
not its keySet()
.
Constructors need to be defined explicitly in the subclass. If a constructor is defined in superclass that doesn't mean that constructor can be used to create an instance of subclass whether you are using reflection or not.
Since your SubClass2 doesn't have constructor with one argument, so when you try to create an instance of it with one argument, it's throwing NoSuchMethodException.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With