How to create instance of subclass with constructor from super class

后端 未结 4 2179
时光取名叫无心
时光取名叫无心 2021-02-19 00:51

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

相关标签:
4条回答
  • 2021-02-19 01:21
    • Use clazz.getDeclaredConstructors() to get all constructors of the class;
    • Iterate through them to find the best applicable constructor, e.g. pick the zero-args one if a single-Object-arg constructor is not available;
    • Invoke that constructor using the appropriate parameters.

    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).

    0 讨论(0)
  • 2021-02-19 01:23

    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.

    0 讨论(0)
  • 2021-02-19 01:26
    1. A superclass has no knowledge of its children.
    2. Constructors are not inherited.

    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:

    1. Lost the reflection
    2. Acquired compile time type safety
    3. Lost the ugly cast (although that was through misuse of reflection)

    N.B. loop through a Map's entrySet() not its keySet().

    0 讨论(0)
  • 2021-02-19 01:32

    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();
                }  
    
    0 讨论(0)
提交回复
热议问题