7

我正在使用抽象工厂来返回具体子类的实例。我想在运行时实例化子类,给定具体类名的字符串。我还需要将参数传递给构造函数。类结构如下:

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

我上面的 attmept 抛出了以下异常:java.lang.NoSuchMethodException: Child.<init>(),然后是堆栈跟踪。

任何帮助表示赞赏。谢谢!

4

2 回答 2

14
Constructor<?> c = Class.forName(childClass).getDeclaredConstructor(constructorParam.getClass());
c.setAccessible(true);
c.newInstance(new Object[] {constructorParam});

getConstructor方法接受Class参数来区分构造函数。但它只返回公共构造函数,所以你需要getDeclaredConstructor(..). 那么你需要setAccessible(true)

于 2011-11-23T21:07:39.700 回答
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);
   }
    }
于 2011-11-23T21:27:58.607 回答