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

bib*_*ibs 7 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Boz*_*zho 14

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

getConstructor方法使用Class参数来区分构造函数.但它只返回公共构造函数,所以你需要getDeclaredConstructor(..).然后你需要setAccessible(true)


jay*_*100 5

错误:您调用了错误的构造函数 - 并且编译器无法帮助您。

您遇到的问题很简单,您正在访问零参数构造函数,而不是带参数的构造函数。请记住,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;
}
Run Code Online (Sandbox Code Playgroud)

当然,添加到您的测试中

    @Test 
    public void testInvoke()
    {
        try{ 
   MyClass.create(1,"test");
   }
   catch(Exception e)
   {
       Assert.fail("Invocation failed : check api for reflection classes in " + MyClass.class);
   }
    }
Run Code Online (Sandbox Code Playgroud)