Criando uma Instância Usando o Nome da Classe e Construtor de Chamada

312

Existe uma maneira de criar uma instância de uma classe específica, dado o nome da classe (dinâmico) e passar parâmetros para seu construtor.

Algo como:

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

Onde "MyAttributeValue"está um argumento para o construtor de MyClass.

TheLameProgrammer
fonte

Respostas:

497

Sim, algo como:

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

Obviamente, isso funcionará apenas para um parâmetro de cadeia única, mas você pode modificá-lo com bastante facilidade.

Observe que o nome da classe deve ser totalmente qualificado, ou seja, incluindo o espaço para nome. Para classes aninhadas, você precisa usar um dólar (pois é isso que o compilador usa). Por exemplo:

package foo;

public class Outer
{
    public static class Nested {}
}

Para obter o Classobjeto para isso, você precisaria Class.forName("foo.Outer$Nested").

Jon Skeet
fonte
5
newInstance()é um método varargs (assim como GetConstructor()), não há necessidade de Objectcriação explícita de matriz.
Joachim Sauer
18
@ Joachim: Eu sei que é varargs, mas como pode ficar complicado quando você tem um Object[]argumento, prefiro criar a matriz explicitamente neste caso.
Jon Skeet
2
clazz.getConstructor (String.class); por que String.class aqui?
Umair A.
2
@ Neutralizer: Sim, mas eu estava respondendo uma pergunta em que não precisava ser dinâmica.
21814 Jon Skeet
3
@ JonSkeet Eu entendo de onde você é, mas não é tão simples - eu olhei para os documentos, mas estava confuso, mas também se eu o testasse e funcionasse - ok, funcionou - mas se não funcionasse, então Eu não teria certeza se o problema era devido a alguma falta de configuração ou algo da minha parte - muitas vezes, ao fazer perguntas tão simples, as pessoas dão petiscos úteis que realmente ajudam. É por isso que um simples "sim que funcionaria - se você fizer dessa maneira" ou "não, não tem jeito", realmente ajuda. Mas o meu entendimento agora é que não há nenhuma maneira
ycomp
97

Você pode usar Class.forName()para obter um Classobjeto da classe desejada.

Em seguida, use getConstructor()para encontrar o Constructorobjeto desejado .

Por fim, chame newInstance()esse objeto para obter sua nova instância.

Class<?> c = Class.forName("mypackage.MyClass");
Constructor<?> cons = c.getConstructor(String.class);
Object object = cons.newInstance("MyAttributeValue");
Joachim Sauer
fonte
81

Você pode usar reflexos

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Peter Lawrey
fonte
3
Se estiver usando o construtor padrão, remova o valor do parâmetro String.class, por exemplo, retorne Class.forName (className) .getConstructor (). NewInstance (arg);
Vijay Kumar
21
@VijayKumar Eu acho que você quer dizer Class.forName(className).getConstructor().newInstance();;)
Peter Lawrey
14

Se a classe tiver apenas um construtor vazio (como Activity ou Fragment etc, classes android):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
tier777
fonte
2
Foi isso que me ajudou. Constructor<?> ctor = clazz.getConstructor(String.class)não parecia funcionar para mim.
Leo C Han
8

ao usar (ou seja), getConstructor(String.lang)o construtor deve ser declarado público. Caso contrário, a NoSuchMethodExceptioné lançada.

se você quiser acessar um construtor não público, precisará usar (por exemplo) getDeclaredConstructor(String.lang).

matthiasboesinger
fonte
4

Maneira muito simples de criar um objeto em Java usando Class<?>argumentos do construtor que passam:

Caso 1: - Aqui está um pequeno código nesta Mainclasse:

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);
    }

}

E aqui está a Baseestrutura da classe:

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+")");
    }

}

Caso 2: - Você pode codificar da mesma forma para o construtor com vários argumentos e copiar o construtor. Por exemplo, passar 3 argumentos como parâmetro para o Baseconstrutor precisará que o construtor seja criado na classe e um código seja alterado acima como:

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

E aqui a classe Base deve se parecer com:

public class Base {

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

Nota: - Não se esqueça de lidar com as várias exceções que precisam ser tratadas no código.

Rahul Raina
fonte
Outra maneira é também clonar () o objeto java existente. Isso cria uma cópia de um objeto Java existente. Nesse caso, você também precisa lidar com o conceito de cópia profunda ou cópia rasa.
Rahul Raina
2

Se alguém estiver procurando uma maneira de criar uma instância de uma classe, apesar da classe seguir o Padrão Singleton, aqui está uma maneira de fazê-lo.

// 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();

Isso funciona apenas para classes que implementam padrão singleton usando um construtor privado.

Omer
fonte
1

Você também pode chamar métodos dentro do objeto criado.

Você pode criar um objeto instantaneamente chamando o primeiro construtor e, em seguida, o primeiro método no objeto criado.

    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);
Hatem Badawi
fonte
Como você saberá que o primeiro construtor usa Stringcomo parâmetro? Torna-se um pouco confuso quando você alterar a ordem de construtor
Farid
@Farid from documentation documentation
Hatem Badawi
Ainda getConstructor(ClassName.class)é melhor, eu acho. Mesmo que a ordem de construtores muda na classe, não há necessidade de encontrar a posição manualmente
Farid
@Farid - c.getDeclaredMethods () [0] .invoke (objeto, Object ... MethodArgs); especifica construtor especial em alguns casos, você pode precisar disso, mas você está certo.
Hatem Badawi
1

Outra resposta útil. Como uso o getConstructor (params) .newInstance (args)?

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

No meu caso, o construtor da minha classe usa o Webdriver como parâmetro, então usado abaixo do código:

return Class.forName("com.page.BillablePage")
    .getConstructor(WebDriver.class)
    .newInstance(this.driver);
user3181500
fonte