Crear una instancia usando el nombre de la clase y llamar al constructor

312

¿Hay alguna manera de crear una instancia de una clase particular dado el nombre de la clase (dinámica) y pasar parámetros a su constructor.

Algo como:

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

Donde "MyAttributeValue"es un argumento para el constructor de MyClass.

TheLameProgrammer
fuente

Respuestas:

497

Sí, algo como:

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

Por supuesto, eso solo funcionará para un único parámetro de cadena, pero puede modificarlo con bastante facilidad.

Tenga en cuenta que el nombre de la clase debe ser totalmente calificado, es decir, incluir el espacio de nombres. Para las clases anidadas, debe usar un dólar (ya que eso es lo que usa el compilador). Por ejemplo:

package foo;

public class Outer
{
    public static class Nested {}
}

Para obtener el Classobjeto para eso, necesitarías Class.forName("foo.Outer$Nested").

Jon Skeet
fuente
55
newInstance()es un método varargs (igual que GetConstructor()), no hay necesidad de Objectcrear una matriz explícita .
Joachim Sauer
18
@Joachim: Sé que son varargs, pero como puede ser complicado cuando tienes una Object[]discusión, prefiero crear la matriz explícitamente en este caso.
Jon Skeet
2
clazz.getConstructor (String.class); ¿Por qué String.class aquí?
Umair A.
2
@Neutralizer: Sí, pero estaba respondiendo una pregunta donde no necesitaba ser dinámico.
Jon Skeet
3
@ JonSkeet Entiendo de dónde vienes, sin embargo, no es tan simple: miré los documentos pero estaba confundido, pero también si lo probé y funcionó, está bien, pero funcionó, pero si no funcionó, entonces No habría estado seguro si el problema se debía a una falta de configuración o algo de mi parte; a menudo, al hacer preguntas tan simples, la gente arroja cositas útiles que realmente ayudan. Es por eso que un simple "sí, eso funcionaría, si lo haces de esta manera" o "no, no hay manera", realmente ayuda. Pero ahora entiendo que no hay manera
ycomp
97

Puede usar Class.forName()para obtener un Classobjeto de la clase deseada.

Luego use getConstructor()para encontrar el Constructorobjeto deseado .

Finalmente, llame newInstance()a ese objeto para obtener su nueva instancia.

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

Puedes usar reflejos

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Peter Lawrey
fuente
3
Si usa el constructor predeterminado, elimine el valor del parámetro String.class, por ejemplo, devuelva Class.forName (className) .getConstructor (). NewInstance (arg);
Vijay Kumar
21
@VijayKumar, creo que quieres decir Class.forName(className).getConstructor().newInstance();;)
Peter Lawrey
14

Si la clase solo tiene un constructor vacío (como Actividad o Fragmento, etc., clases de Android):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
tier777
fuente
2
Esto es lo que me ayudó. Constructor<?> ctor = clazz.getConstructor(String.class)no parece funcionar para mi
Leo C Han
8

cuando se usa (es decir), getConstructor(String.lang)el constructor debe ser declarado público. De lo contrario, NoSuchMethodExceptionse arroja un.

si desea acceder a un constructor no público , debe usarlo en su lugar (es decir) getDeclaredConstructor(String.lang).

matthiasboesinger
fuente
4

Manera muy simple de crear un objeto en Java usando Class<?>con argumento (s) de constructor pasando:

Caso 1: - Aquí, hay un código pequeño en esta Mainclase:

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

}

Y, aquí está la Baseestructura de clases:

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: - Usted puede codificar de manera similar para constructor con argumento múltiple y constructor de copia. Por ejemplo, pasar 3 argumentos como parámetro al Baseconstructor necesitará que el constructor se cree en clase y un cambio de código arriba como:

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

Y aquí la clase Base debería verse de alguna manera:

public class Base {

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

Nota: - No olvide manejar las diversas excepciones que deben manejarse en el código.

Rahul Raina
fuente
Otra forma es también clonar () el objeto java existente. Esto crea una copia de un objeto Java existente. Para este caso, también debe manejar el concepto de copia profunda o copia superficial.
Rahul Raina
2

Si alguien está buscando una manera de crear una instancia de una clase a pesar de que la clase sigue el Patrón Singleton, aquí hay una manera de hacerlo.

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

Esto solo funciona para clases que implementan un patrón singleton usando un constructor privado.

Omer
fuente
1

También puede invocar métodos dentro del objeto creado.

Puede crear objetos instantáneamente invocando el primer limitador y luego invocar el primer método en el objeto creado.

    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
fuente
¿Cómo sabrá que el primer constructor toma Stringcomo parámetro? Se vuelve un poco desordenado cuando cambia el orden del constructor
Farid
@Farid de la documentación de la clase
Hatem Badawi
Aún así getConstructor(ClassName.class)es mejor, supongo. Incluso si el orden de los constructores cambia en clase, no es necesario encontrar la posición manualmente
Farid
@Farid - c.getDeclaredMethods () [0] .invoke (objeto, Objeto ... MétodoArgs); especifica un constructor especial en algunos casos, puede que necesite esto; pero tiene razón.
Hatem Badawi
1

Otra respuesta útil. ¿Cómo uso getConstructor (params) .newInstance (args)?

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

En mi caso, el constructor de mi clase toma Webdriver como parámetro, por lo que se usa debajo del código:

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