我正在使用抽象工厂来返回具体子类的实例。我希望在给定具体类名的String的情况下在运行时实例化子类。我还需要将参数传递给构造函数。类结构如下:
abstract class Parent {
private static HashMap<String, Child> instances = new HashMap<String,Child>()
private Object constructorParameter;
public static Child factory(String childName, Object constructorParam){
if(instances.keyExists(childName)){
return instances.get(childName);
}
//Some code here to instantiate the Child using constructorParam,
//then save Child into the HashMap, and then return the Child.
//Currently, I am doing:
Child instance = (Child) Class.forName(childClass).getConstructor().newInstance(new Object[] {constructorParam});
instances.put(childName, instance);
return instance;
}
//Constructor is protected so unrelated classes can't instantiate
protected Parent(Object param){
constructorParameter = param;
}
}//end Parent
class Child extends Parent {
protected Child(Object constructorParameter){
super(constructorParameter);
}
}
我上面提到的是抛出以下异常:java.lang.NoSuchMethodException: Child.<init>()
,然后是堆栈跟踪。
感谢任何帮助。谢谢!
答案 0 :(得分:13)
Constructor<?> c = Class.forName(childClass).getDeclaredConstructor(constructorParam.getClass());
c.setAccessible(true);
c.newInstance(new Object[] {constructorParam});
getConstructor
方法使用Class
参数来区分构造函数。但它只返回公共构造函数,因此您需要getDeclaredConstructor(..)
。那么您需要setAccessible(true)
答案 1 :(得分:4)
错误:您正在调用错误的构造函数 - 编译器无法帮助您。
您遇到的问题只是您访问的是零参数构造函数,而不是带参数的构造函数。请记住,java中的构造函数最终只是方法,虽然是特殊的 - 并且通过反射,所有的注意都是关闭的 - 如果你做一些愚蠢的事情,编译器将无法帮助你。在您的情况下,您同时遇到了范围问题和方法签名问题。
如何解决此问题,并且永远不必在此应用程序中再次处理它
最好将构造函数调用包装在一个可以直接测试的静态帮助器方法中,然后在我的单元测试中为它们显式地进行测试,因为如果构造函数发生了变化并且你忘记了要更新您的反射代码,您将再次看到这些神秘错误再次出现。
您也可以按如下方式调用构造函数:
public static Child create(Integer i, String s) throws Exception
{
Constructor c = Class.forName(childClass).getConstructor(new Object[]{Integer.class, String.class});
c.setAccessible(true);
Child instance = (Child) c.newInstance(new Object[]{i , s}) ;
return instance;
}
当然会添加到您的测试中
@Test
public void testInvoke()
{
try{
MyClass.create(1,"test");
}
catch(Exception e)
{
Assert.fail("Invocation failed : check api for reflection classes in " + MyClass.class);
}
}