Класс Instantiate Java во время выполнения с параметрами - PullRequest
7 голосов
/ 24 ноября 2011

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

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 ]

13 голосов
/ 24 ноября 2011
Constructor<?> c = Class.forName(childClass).getDeclaredConstructor(constructorParam.getClass());
c.setAccessible(true);
c.newInstance(new Object[] {constructorParam});

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

4 голосов
/ 24 ноября 2011

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

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