如何使用超类中的构造函数创建子类的实例

时间:2016-03-09 06:33:15

标签: java

我想为类是超类的子类创建一个注册表。这些类存储在充当注册表的映射中。根据一个键从注册表中选择一个类,并通过反射创建该类的实例。

我想根据超类的构造函数(带有1个参数)来实例化一个类。它只有在我在子类中声明构造函数时才有效。

有没有办法使用超类的构造函数来实例化类?有没有办法让代码类型安全?

示例代码:

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);
    }
}

该示例提供以下控制台输出:

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)

4 个答案:

答案 0 :(得分:12)

  1. 超类不了解其子女。
  2. 构造函数不是继承的。
  3. 因此,如果不对子类ctor进行假设,则无法编写所需的代码。

    那你能做什么?使用抽象工厂模式。

    我们可以创建interface Factory

    @FunctionalInterface
    public interface SuperclassFactory {
        Superclass newInstance(Object o);
    }
    

    你可以在Factory上创建多个方法,但这会使lambda不那么整洁。

    现在你有一个Map<Integer, SuperclassFactory>,并填充它:

    Map<Integer,SuperclassFactory> registry = new HashMap<>();
    registry.put(0, SubClass1::new);
    registry.put(1, SubClass2::new);
    

    因此,为了使用此Map,您只需执行以下操作:

    for(final Map.Entry<Integer,SuperclassFactory> e: registry.entrySet()) {
        //...
        final BaseClass instance = e.getValue().newInstance(e.getKey());
        //...
    }
    

    如果您的子类没有相应的ctor,则此代码将无法编译,因为不会有可用的ctor引用。这是Good Thing(TM)。要使用当前Subclass2进行编译,您需要使用:

    registry.put(1, obj -> new SubClass2());
    

    现在我们有:

    1. 失去了反思
    2. 获取编译时类型安全
    3. 失去了丑陋的演员阵容(虽然这是通过滥用反射)
    4. N.B。循环遍历Map的{​​{1}}而不是entrySet()

答案 1 :(得分:1)

需要在子类中显式定义构造函数。如果在超类中定义构造函数并不意味着无论是否使用反射,构造函数都可用于创建子类的实例。

由于您的SubClass2没有带有一个参数的构造函数,因此当您尝试使用一个参数创建它的实例时,它会抛出NoSuchMethodException。

答案 2 :(得分:1)

  • 使用clazz.getDeclaredConstructors()获取该类的所有构造函数;
  • 遍历它们以找到最适用的构造函数,例如:如果单个Object-arg构造函数不可用,则选择零参数;
  • 使用适当的参数调用该构造函数。

这是完全安全的,因为您无法预先知道给定类是否存在任何适用的公共构造函数,例如: ctor可能是私有的,或者可用的构造函数可能不接受你想要的类型的参数(例如需要一个String,但你只有一个Object)。

答案 3 :(得分:0)

您正在使用此行来获取构造函数

clazz.getDeclaredConstructor( Object.class);

但是你的Subclass2没有一个参数构造函数,因此抛出了异常。

使用 clazz.getDeclaredConstructors() 方法,并根据参数计数调用构造函数。

            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();
            }