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 dep
clazz.getDeclaredConstructors()
to get all constructors of the class;There is no way for this to be entirely safe, since you can't know in advance whether any applicable public constructors exist for a given class, e.g. the ctor might be private, or the available constructors might not accept parameters of the type you want (e.g. needs a String, but you only have an Object).
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.
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()
.
You are using this line to get the constructor
clazz.getDeclaredConstructor( Object.class);
But your Subclass2 does not have a single argument constructor hence the exception is thrown.
Use clazz.getDeclaredConstructors() method instead and invoke constructor based on parameters count.
BaseClass instance;
// get constructor with parameter
Constructor constructor = clazz.getDeclaredConstructors()[0];
if (constructor.getParameterCount() == 1) {
instance = (BaseClass) constructor.newInstance(key);
} else {
instance = (BaseClass) constructor.newInstance();
}