[英]How to create instance of subclass with constructor from super class
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. 我想根据超类的构造函数(带有1个参数)来实例化一个类。 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)
Therefore, without making assumptions about the subclass ctor, you cannot write the code that you want. 因此,在不对子类ctor进行假设的情况下,您无法编写所需的代码。
So what can you do? 所以,你可以做什么? Use an Abstract Factory pattern.
使用抽象工厂模式。
We can create an interface Factory
: 我们可以创建一个
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. 你可以在
Factory
上创建多个方法,但这会使lambda不那么整洁。
Now you have a Map<Integer, SuperclassFactory>
, and populate it: 现在你有一个
Map<Integer, SuperclassFactory>
,并填充它:
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: 因此,为了使用此
Map
您只需执行以下操作:
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. 如果您的子类没有相应的ctor,则此代码将无法编译,因为不会有可用的ctor引用。 This is Good Thing (TM).
这是Good Thing(TM)。 In order to compile with the current
Subclass2
you would need to use: 要使用当前的
Subclass2
进行编译,您需要使用:
registry.put(1, obj -> new SubClass2());
So now we have: 所以现在我们有:
NB loop through a Map
's entrySet()
not its keySet()
. NB循环通过
Map
的entrySet()
而不是它的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. 由于您的SubClass2没有带有一个参数的构造函数,因此当您尝试使用一个参数创建它的实例时,它会抛出NoSuchMethodException。
clazz.getDeclaredConstructors()
to get all constructors of the class; clazz.getDeclaredConstructors()
获取类的所有构造函数; 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, eg the ctor might be private, or the available constructors might not accept parameters of the type you want (eg needs a String, but you only have an Object). 由于您无法预先知道给定类是否存在任何适用的公共构造函数,例如ctor可能是私有的,或者可用的构造函数可能不接受您想要的类型的参数,因此无法完全安全。 (例如需要一个String,但你只有一个Object)。
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. 但是你的Subclass2没有一个参数构造函数,因此引发了异常。
Use clazz.getDeclaredConstructors() method instead and invoke constructor based on parameters count. 请改用clazz.getDeclaredConstructors()方法,并根据参数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();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.