Java Instantiate Class во время выполнения с параметрами


Я использую абстрактную фабрику для возврата экземпляров конкретных подклассов.Я хотел бы создать экземпляр подкласса во время выполнения с заданной строкой конкретного имени класса. Мне также нужно передать параметр конструкторам. Структура классов выглядит следующим образом:

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>(), за которым следует трассировка стека.

Любая помощь ценится. Спасибо!

2 6

2 ответа:

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

Метод getConstructor принимает аргументы Class для различения конструкторов. Но он возвращает только открытые конструкторы, поэтому вам понадобится getDeclaredConstructor(..). Тогда вам понадобится setAccessible(true)

Ошибка : вы вызываете неправильный конструктор-и компилятор не может вам помочь.

Проблема, с которой вы столкнулись, заключается просто в том, что вы обращались к конструктору с нулевым аргументом, а не к конструктору с аргументами. Помните, что конструкторы в 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);
   }
    }