Création d'une instance à l'aide du nom de classe et appelant le constructeur

312

Existe-t-il un moyen de créer une instance d'une classe particulière compte tenu du nom de classe (dynamique) et de passer des paramètres à son constructeur.

Quelque chose comme:

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

"MyAttributeValue"est un argument pour le constructeur de MyClass.

TheLameProgrammer
la source

Réponses:

497

Oui, quelque chose comme:

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

Bien sûr, cela ne fonctionnera que pour un seul paramètre de chaîne, mais vous pouvez le modifier assez facilement.

Notez que le nom de classe doit être complet, c'est-à-dire inclure l'espace de noms. Pour les classes imbriquées, vous devez utiliser un dollar (car c'est ce que le compilateur utilise). Par exemple:

package foo;

public class Outer
{
    public static class Nested {}
}

Pour obtenir l' Classobjet pour cela, vous auriez besoin Class.forName("foo.Outer$Nested").

Jon Skeet
la source
5
newInstance()est une méthode varargs (tout comme GetConstructor()), il n'y a pas besoin de Objectcréation de tableau explicite .
Joachim Sauer
18
@Joachim: Je sais que ce sont des varargs, mais comme cela peut devenir délicat lorsque vous avez un Object[]argument, je préfère créer le tableau explicitement dans ce cas.
Jon Skeet
2
clazz.getConstructor (String.class); pourquoi String.class ici?
Umair A.
2
@Neutralizer: Oui, mais je répondais à une question où il n'avait pas besoin d'être dynamique.
Jon Skeet
3
@JonSkeet Je comprends d'où vous venez, mais ce n'est pas aussi simple - j'ai regardé les documents mais j'étais confus, mais aussi si je l'ai testé et que ça a fonctionné - ok alors ça a fonctionné - mais si ça n'a pas fonctionné alors J'aurais été incertain si le problème était dû à un manque de configuration ou à quelque chose de ma part - souvent en posant des questions aussi simples, les gens jettent des informations utiles qui aident vraiment. C'est pourquoi un simple «oui qui fonctionnerait - si vous le faites de cette façon» ou «non, il n'y a aucun moyen», aide vraiment. Mais je comprends maintenant qu'il n'y a aucun moyen
ycomp
97

Vous pouvez utiliser Class.forName()pour obtenir un Classobjet de la classe souhaitée.

Utilisez ensuite getConstructor()pour trouver l' Constructorobjet souhaité .

Enfin, appelez newInstance()cet objet pour obtenir votre nouvelle instance.

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

Vous pouvez utiliser des réflexions

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Peter Lawrey
la source
3
Si vous utilisez le constructeur par défaut, supprimez la valeur du paramètre String.class, par exemple, retournez Class.forName (className) .getConstructor (). NewInstance (arg);
Vijay Kumar
21
@VijayKumar Je pense que tu veux dire Class.forName(className).getConstructor().newInstance();;)
Peter Lawrey
14

Si la classe n'a qu'un seul constructeur vide (comme Activity ou Fragment etc, classes android):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
tier777
la source
2
C'est ça qui m'a aidé. Constructor<?> ctor = clazz.getConstructor(String.class)ne semblait pas fonctionner pour moi.
Leo C Han
8

lors de l'utilisation (ie), getConstructor(String.lang)le constructeur doit être déclaré public. Sinon, un NoSuchMethodExceptionest lancé.

si vous voulez accéder à un constructeur non public, vous devez utiliser à la place (ie) getDeclaredConstructor(String.lang).

matthiasboesinger
la source
4

Un moyen très simple de créer un objet en Java en utilisant Class<?>les arguments du constructeur en passant:

Cas 1: - Voici un petit code dans cette 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);
    }

}

Et, voici la Basestructure des classes:

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

}

Cas 2: - Vous pouvez coder de manière similaire pour le constructeur avec plusieurs arguments et copier le constructeur. Par exemple, la transmission de 3 arguments en tant que paramètre au Baseconstructeur nécessitera la création du constructeur en classe et un changement de code ci-dessus comme suit:

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

Et ici, la classe Base devrait ressembler à:

public class Base {

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

Remarque: - N'oubliez pas de gérer les différentes exceptions qui doivent être traitées dans le code.

Rahul Raina
la source
Une autre façon est également de cloner () l'objet java existant. Cela crée une copie d'un objet Java existant. Dans ce cas, vous devez également gérer le concept de copie profonde ou de copie superficielle.
Rahul Raina
2

Si quelqu'un cherche un moyen de créer une instance d'une classe malgré la classe suivant le modèle Singleton, voici un moyen de le faire.

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

Cela ne fonctionne que pour les classes qui implémentent un modèle singleton à l'aide d'un constructeur privé.

Omer
la source
1

Vous pouvez également appeler des méthodes à l'intérieur de l'objet créé.

Vous pouvez créer un objet instantanément en appelant le premier constructeur, puis en invoquant la première méthode dans l'objet créé.

    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
la source
Comment saurez-vous que le tout premier constructeur prend Stringcomme paramètre? Devient un peu désordonné lorsque vous changez l'ordre du constructeur
Farid
@Farid de la documentation de la classe
Hatem Badawi
C'est encore getConstructor(ClassName.class)mieux, je suppose. Même si l'ordre des constructeurs change en classe, pas besoin de trouver la position manuellement
Farid
@Farid - c.getDeclaredMethods () [0] .invoke (object, Object ... MethodArgs); spécifie un constructeur spécial dans certains cas, vous pouvez en avoir besoin, mais vous avez raison.
Hatem Badawi
1

Une autre réponse utile. Comment utiliser getConstructor (params) .newInstance (args)?

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

Dans mon cas, le constructeur de ma classe prend Webdriver comme paramètre, donc utilisé ci-dessous le code:

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