Создание экземпляра с использованием имени класса и вызывающего конструктора

312

Есть ли способ создать экземпляр определенного класса с заданным именем класса (динамический) и передать параметры его конструктору.

Что-то вроде:

Object object = createInstance("mypackage.MyClass","MyAttributeValue");

Где "MyAttributeValue"аргумент для конструктора MyClass.

TheLameProgrammer
источник

Ответы:

497

Да, что-то вроде:

Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(new Object[] { ctorArgument });

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

Обратите внимание, что имя класса должно быть полностью определенным, то есть включать пространство имен. Для вложенных классов вам нужно использовать доллар (так как это то, что использует компилятор). Например:

package foo;

public class Outer
{
    public static class Nested {}
}

Чтобы получить Classобъект для этого, вам нужно Class.forName("foo.Outer$Nested").

Джон Скит
источник
5
newInstance()это метод varargs (так же, как GetConstructor()), нет необходимости явного Objectсоздания массива.
Иоахим Зауэр
18
@Joachim: я знаю, что это varargs, но так как это может быть сложно, когда у вас есть Object[]аргумент, я предпочитаю создавать массив явно в этом случае.
Джон Скит
2
clazz.getConstructor (String.class); почему String.class здесь?
Умайр А.
2
@Neutralizer: Да, но я отвечал на вопрос, где он не должен быть динамичным.
Джон Скит
3
@JonSkeet Я понимаю, откуда вы, но не все так просто - я посмотрел на документы, но был смущен, но также, если я проверял и работал - хорошо, тогда работал - но если не работал, то Я был бы не уверен, если бы проблема была из-за некоторой нехватки конфигурации или чего-то с моей стороны - часто, задавая такие простые вопросы, люди добавляют полезные лакомые кусочки, которые действительно помогают. Вот почему простое «да, это сработает - если вы сделаете это таким образом» или «нет, это никак», действительно помогает. Но теперь я понимаю, что нет никакого пути
комп
97

Вы можете использовать, Class.forName()чтобы получить Classобъект нужного класса.

Затем используйте, getConstructor()чтобы найти нужный Constructorобъект.

Наконец, вызовите newInstance()этот объект, чтобы получить новый экземпляр.

Class<?> c = Class.forName("mypackage.MyClass");
Constructor<?> cons = c.getConstructor(String.class);
Object object = cons.newInstance("MyAttributeValue");
Йоахим Зауэр
источник
81

Вы можете использовать отражения

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Питер Лори
источник
3
Если используется конструктор по умолчанию, удалите значение параметра String.class, например, верните Class.forName (className) .getConstructor (). NewInstance (arg);
Виджай Кумар
21
@VijayKumar Я думаю, что вы имеете в виду Class.forName(className).getConstructor().newInstance();;)
Питер Лоури
14

Если в классе есть только один пустой конструктор (например, Activity или Fragment и т. Д., Классы Android):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
tier777
источник
2
Это то, что помогло мне. Constructor<?> ctor = clazz.getConstructor(String.class)не похоже на работу для меня.
Лео С Хань,
8

при использовании (то есть) getConstructor(String.lang)конструктор должен быть объявлен публичным. В противном случае NoSuchMethodExceptionвыбрасывается.

если вы хотите получить доступ к не публичному конструктору, вы должны использовать вместо этого (то есть) getDeclaredConstructor(String.lang).

matthiasboesinger
источник
4

Очень простой способ создать объект в Java, используя Class<?>передачу аргументов конструктора:

Случай 1: - Вот небольшой код в этом Mainклассе:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Main {

    public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

        // Get class name as string.
        String myClassName = Base.class.getName();
        // Create class of type Base.
        Class<?> myClass = Class.forName(myClassName);
        // Create constructor call with argument types.
        Constructor<?> ctr = myClass.getConstructor(String.class);
        // Finally create object of type Base and pass data to constructor.
        String arg1 = "My User Data";
        Object object = ctr.newInstance(new Object[] { arg1 });
        // Type-cast and access the data from class Base.
        Base base = (Base)object;
        System.out.println(base.data);
    }

}

И вот Baseструктура класса:

public class Base {

    public String data = null;

    public Base() 
    {
        data = "default";
        System.out.println("Base()");
    }

    public Base(String arg1) {
        data = arg1;
        System.out.println("Base("+arg1+")");
    }

}

Случай 2: - Вы можете аналогичным образом кодировать конструктор с несколькими аргументами и конструктор копирования. Например, передача 3 аргументов в качестве параметра Baseконструктору потребует создания конструктора в классе и изменения кода выше как:

Constructor<?> ctr = myClass.getConstructor(String.class, String.class, String.class);
Object object = ctr.newInstance(new Object[] { "Arg1", "Arg2", "Arg3" }); 

И здесь базовый класс должен выглядеть так:

public class Base {

    public Base(String a, String b, String c){
        // This constructor need to be created in this case.
    }   
}

Примечание: - Не забудьте обработать различные исключения, которые должны быть обработаны в коде.

Рахул Райна
источник
Другим способом является также clone () существующий объект Java. Это создает копию существующего объекта Java. В этом случае вам также придется работать с концепцией «Глубокая копия» или «Мелкая копия».
Рахул Райна
2

Если кто-то ищет способ создания экземпляра класса, несмотря на то, что класс следует шаблону Singleton, вот способ сделать это.

// Get Class instance
Class<?> clazz = Class.forName("myPackage.MyClass");

// Get the private constructor.
Constructor<?> cons = clazz.getDeclaredConstructor();

// Since it is private, make it accessible.
cons.setAccessible(true);

// Create new object. 
Object obj = cons.newInstance();

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

Omer
источник
1

Вы также можете вызывать методы внутри созданного объекта.

Вы можете мгновенно создать объект, вызвав первый конструктор, а затем вызвав первый метод в созданном объекте.

    Class<?> c = Class.forName("mypackage.MyClass");
    Constructor<?> ctor = c.getConstructors()[0];
    Object object=ctor.newInstance(new Object[]{"ContstractorArgs"});
    c.getDeclaredMethods()[0].invoke(object,Object... MethodArgs);
Хатем Бадави
источник
Как вы узнаете, что самый первый конструктор принимает Stringв качестве параметра? Становится немного грязно, когда вы меняете порядок конструктора
Фарид
@ Фарид из учебной документации
Хатем Бадави
Все getConstructor(ClassName.class)еще лучше, я думаю. Даже если порядок конструкторов меняется в классе, нет необходимости искать позицию вручную
Фарид
@Farid - c.getDeclaredMethods () [0] .invoke (object, Object ... MethodArgs); указывает специальный конструктор, в некоторых случаях это может вам понадобиться, но вы правы.
Хатем Бадави
1

Еще один полезный ответ. Как мне использовать getConstructor (params) .newInstance (args)?

return Class.forName(**complete classname**)
    .getConstructor(**here pass parameters passed in constructor**)
    .newInstance(**here pass arguments**);

В моем случае конструктор моего класса принимает Webdriver в качестве параметра, поэтому используется следующий код:

return Class.forName("com.page.BillablePage")
    .getConstructor(WebDriver.class)
    .newInstance(this.driver);
user3181500
источник