Как создать экземпляр с конструктором, выбранным пользователем?

#java #reflection

#java #отражение

Вопрос:

Я работал над тем, чтобы лучше понять инструменты отражения Java, и сейчас пытаюсь создать новый экземпляр класса с конструктором, который был выбран пользователем в выпадающем списке.

   //ask user what to name the new instance of chosen Class ( passed into this method )
            String instanceName = JOptionPane.showInputDialog(null, "Under what name would you like to store this Instance?", 
                    "Create an Instance of a Class", JOptionPane.QUESTION_MESSAGE);

            Object chosenC = JOptionPane.showInputDialog(null, "Please choose a Constructor for"   chosenClass, 
                    "Create an Instance of a Class", 3, null, getConstructors(chosenClass), JOptionPane.QUESTION_MESSAGE);

            Constructor c = (Constructor)chosenC;
            Class[] params = c.getParameterTypes(); //get the parameters of the chosen Constructor


    try {

            //create the instance with the correct constructor
            instance = c.newInstance((Object[])params); 

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e){
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } 
  

Прямо сейчас я получаю исключение ArgumentTypeMismatch, если я выбираю что-либо, кроме конструктора по умолчанию (без параметров). Я упускаю что-то вопиюще очевидное?

Спасибо за любую помощь.

РЕДАКТИРОВАТЬ Спасибо всем за ваши ответы.

Но тогда как бы я запросил параметры? Если я получу длину массива params, то мне нужно будет определить, к какому типу класса относится каждый индекс, и попросить пользователя ввести значение в правильной форме? Тогда как бы я передал этот новый массив в параметр newInstance?

Комментарии:

1. Обычно такого рода динамическое создание объекта лучше всего выполнять с классами, у которых есть конструктор по умолчанию. Таким образом, вы можете создать объект, а затем позже задать любые параметры, которые вы хотите, с помощью установщиков и т.д. В этом случае нужно будет выполнить цикл, хотя все params и для каждого класса там создаются clazz.newInstance(...) … что может быть приятной бесконечной бездной

2. @Frank71 — это приложение для разработчиков или у вас есть конечный пользователь? В последнем случае я бы не оставил определение поведения на усмотрение Reflection API, а указал бы в коде конструктор / поведение каждой опции из выпадающего списка.

Ответ №1:

Задан класс с конструктором, подобным этому:

 MyClass(String s, Long l)
  

Ваш код фактически вызывает следующее:

 new MyClass(String.class, Long.class)
  

Где он должен вызывать:

 new MyClass("", 1L)
  

Вам нужен экземпляр каждого из типов параметров конструктора, но вы передаете только классы. Интересно, что если ваш конструктор принимает только экземпляры Class , это сработало бы, но дало бы неожиданные результаты.

Комментарии:

1. Хороший пример. но я должен придраться к последнему предложению. Это скомпилировалось бы, но, скорее всего, это было бы не то, чего кто-либо хотел, поэтому говорить «это сработало бы», я думаю, не совсем точно.

Ответ №2:

Вы передаете класс [] конструктору, когда вам нужен массив экземпляров. Например, если вашим конструктором является Foo(String), вы хотите сделать что-то вроде

 c.newInstance(new Object[] {"aString"});
  

То, что вы делаете, это,

 c.newInstance(new Object[] {String.class});
  

Ответ №3:

Ваши params должны быть аргументами, переданными конструктору. Все ваши параметры являются ссылками на класс, поэтому, если все ваши параметры не являются Class a, Class b, Class c , это не сработает.

Возможно, вам следует разрешить пользователю определять, какими должны быть значения.

Ответ №4:

Вызов:

 instance = c.newInstance((Object[])params); 
  

здесь вы должны передать аргументы для конструктора, а не типы, которые вы получаете при вызове:

 Class[] params = c.getParameterTypes();
  

Если ваш конструктор является:

 public MyConstructor( int foo, String bar ) {
//...
}
  

затем вы должны передать параметры как (например):

 Object [] params = new Object [] { 10, "someString" };
  

Ответ №5:

Когда вы вызываете newInstance экземпляр конструктора, вам нужно передать параметры правильных классов. Вы предоставляете Class сами объекты в качестве параметров.

Например, если ваш конструктор является

 Person(String firstName, String lastName)
  

Вы бы вызвали

 new Person("John", "Doe");
  

Или, в терминах отражения

 c.newInstance("John", "Doe");
  

Но ваш код на самом деле выполняет

 c.newInstance(String.class, String.class);
  

Если вы хотите, чтобы пользователь выбрал конструктор, вам также нужно будет запросить у него некоторые параметры.