我想为类是超类的子类创建一个注册表。这些类存储在充当注册表的映射中。根据一个键从注册表中选择一个类,并通过反射创建该类的实例。
我想根据超类的构造函数(带有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)
答案 0 :(得分:12)
因此,如果不对子类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());
现在我们有:
N.B。循环遍历Map
的{{1}}而不是entrySet()
。
答案 1 :(得分:1)
需要在子类中显式定义构造函数。如果在超类中定义构造函数并不意味着无论是否使用反射,构造函数都可用于创建子类的实例。
由于您的SubClass2没有带有一个参数的构造函数,因此当您尝试使用一个参数创建它的实例时,它会抛出NoSuchMethodException。
答案 2 :(得分:1)
clazz.getDeclaredConstructors()
获取该类的所有构造函数; 这是完全安全的,因为您无法预先知道给定类是否存在任何适用的公共构造函数,例如: 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();
}