【问题标题】:Java Instantiate Class at Runtime with parametersJava 在运行时使用参数实例化类
【发布时间】:2012-01-05 03:36:03
【问题描述】:

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

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.&lt;init&gt;(),然后是堆栈跟踪。

感谢任何帮助。谢谢!

【问题讨论】:

    标签: java dynamic-typing abstract-factory


    【解决方案1】:

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

    您遇到的问题只是您访问的是零参数构造函数,而不是带有参数的构造函数。请记住,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 抱歉,我遗漏了那个“次要”细节。动态构造函数调用当然需要一个对象数组作为其参数,它必须与您从 Class.getConstructor... 方法获取的构造函数签名相匹配。
    【解决方案2】:
    Constructor<?> c = Class.forName(childClass).getDeclaredConstructor(constructorParam.getClass());
    c.setAccessible(true);
    c.newInstance(new Object[] {constructorParam});
    

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

    【讨论】:

    • 我试过了,但仍然看到同样的错误。我需要更改任何构造函数签名吗?目前,签名并不明确期望 Object 类型的参数,而是更具体的参数。
    • 您的 constructorParam.getClass() 应该返回您期望的确切参数类型
    • 根据您的示例,我不确定 constructorParam.getClass() 的作用。你能更好地解释你的答案吗?谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-29
    • 1970-01-01
    • 1970-01-01
    • 2010-09-09
    • 2020-06-25
    • 2021-10-06
    • 1970-01-01
    相关资源
    最近更新 更多