Java在运行时使用参数实例化类

时间:2011-11-23 21:04:01

标签: java dynamic-typing abstract-factory

我正在使用抽象工厂来返回具体子类的实例。我希望在给定具体类名的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>(),然后是堆栈跟踪。

感谢任何帮助。谢谢!

2 个答案:

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