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

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

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

任何帮助都将不胜感激。谢谢!

2个回答

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

getConstructor 方法接受 Class 参数来区分构造函数。但是它仅返回公共构造函数,因此您需要使用 getDeclaredConstructor(..)。然后您需要设置 setAccessible(true)


我尝试过这个,但仍然看到相同的错误。我需要改变任何构造函数签名吗?目前的签名并没有明确地期望Object类型的参数,而是更具体的类型。 - bibs
你的 constructorParam.getClass() 应该返回你所期望的准确参数类型 - Bozho
根据你的示例,我不确定 constructorParam.getClass() 是做什么的。你能更好地解释一下你的答案吗?谢谢! - trusktr

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

当然,还要添加到您的测试中。
    @Test 
    public void testInvoke()
    {
        try{ 
   MyClass.create(1,"test");
   }
   catch(Exception e)
   {
       Assert.fail("Invocation failed : check api for reflection classes in " + MyClass.class);
   }
    }

你在这里实际上是在哪里传递参数给构造函数的? - bibs
@bibs 抱歉,我漏掉了那个“小”细节。动态构造函数调用当然需要一个对象数组作为其参数,这个参数必须匹配你从 Class.getConstructor... 方法获取的构造函数签名。 - jayunit100

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接