Erstellen einer Instanz mit dem Klassennamen und Aufrufen des Konstruktors

312

Gibt es eine Möglichkeit, eine Instanz einer bestimmten Klasse mit dem Klassennamen (dynamisch) zu erstellen und Parameter an ihren Konstruktor zu übergeben?

Etwas wie:

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

Wo "MyAttributeValue"ist ein Argument an den Konstruktor von MyClass.

TheLameProgrammer
quelle

Antworten:

497

Ja, so etwas wie:

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

Das funktioniert natürlich nur für einen einzelnen String-Parameter, aber Sie können ihn ziemlich einfach ändern.

Beachten Sie, dass der Klassenname vollständig qualifiziert sein muss, dh einschließlich des Namespace. Für verschachtelte Klassen müssen Sie einen Dollar verwenden (wie es der Compiler verwendet). Zum Beispiel:

package foo;

public class Outer
{
    public static class Nested {}
}

Um das ClassObjekt dafür zu erhalten , benötigen Sie Class.forName("foo.Outer$Nested").

Jon Skeet
quelle
5
newInstance()ist eine varargs-Methode (genau wie GetConstructor()), es ist keine explizite ObjectArray-Erstellung erforderlich.
Joachim Sauer
18
@ Joachim: Ich weiß, dass es varargs ist, aber da es schwierig werden kann, wenn Sie ein Object[]Argument haben, ziehe ich es vor, das Array in diesem Fall explizit zu erstellen.
Jon Skeet
2
clazz.getConstructor (String.class); warum String.class hier?
Umair A.
2
@Neutralizer: Ja, aber ich habe eine Frage beantwortet, bei der es nicht dynamisch sein musste.
Jon Skeet
3
@ JonSkeet Ich verstehe, woher du kommst, aber es ist nicht ganz so einfach - ich habe mir die Dokumente angesehen, war aber verwirrt, aber auch, wenn ich es getestet habe und es funktioniert hat - ok, dann hat es funktioniert - aber wenn es dann nicht funktioniert hat Ich wäre mir nicht sicher gewesen, ob das Problem auf einen Mangel an Konfiguration oder auf etwas von meiner Seite zurückzuführen ist - oft werfen die Leute bei so einfachen Fragen nützliche Leckerbissen ein, die wirklich helfen. Deshalb hilft ein einfaches "Ja, das würde funktionieren - wenn Sie es so machen" oder "Nein, es gibt keinen Weg" wirklich. Aber ich verstehe jetzt, dass es keinen Weg gibt
ycomp
97

Sie können verwenden Class.forName(), um ein ClassObjekt der gewünschten Klasse abzurufen.

Verwenden Sie dann getConstructor(), um das gewünschte zu findenConstructor Objekt .

Rufen Sie newInstance()abschließend dieses Objekt auf, um Ihre neue Instanz abzurufen.

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

Sie können Reflexionen verwenden

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Peter Lawrey
quelle
3
Wenn Sie den Standardkonstruktor verwenden, entfernen Sie den Parameterwert String.class, z. B. return Class.forName (className) .getConstructor (). NewInstance (arg);
Vijay Kumar
21
@ VijayKumar Ich denke du meinst Class.forName(className).getConstructor().newInstance();;)
Peter Lawrey
14

Wenn die Klasse nur einen leeren Konstruktor hat (wie Aktivität oder Fragment usw., Android-Klassen):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
tier777
quelle
2
Das hat mir geholfen. Constructor<?> ctor = clazz.getConstructor(String.class)schien nicht für mich zu arbeiten.
Leo C Han
8

Bei Verwendung (dh) muss getConstructor(String.lang)der Konstruktor als öffentlich deklariert werden. Ansonsten NoSuchMethodExceptionwird a geworfen.

Wenn Sie auf einen nicht öffentlichen Konstruktor zugreifen möchten, müssen Sie stattdessen (dh) verwenden getDeclaredConstructor(String.lang).

matthiasboesinger
quelle
4

Sehr einfache Möglichkeit, ein Objekt in Java zu erstellen, indem Class<?>Konstruktorargumente übergeben werden:

Fall 1: - Hier ist ein kleiner Code in dieser MainKlasse:

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

}

Und hier ist die BaseKlassenstruktur:

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

}

Fall 2: - Sie können ähnlich für den Konstruktor mit mehreren Argumenten codieren und den Konstruktor kopieren. Wenn Sie beispielsweise 3 Argumente als Parameter an den BaseKonstruktor übergeben, muss der Konstruktor in der Klasse erstellt und der Code wie folgt geändert werden:

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

Und hier sollte die Basisklasse irgendwie aussehen:

public class Base {

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

Hinweis: - Vergessen Sie nicht, die verschiedenen Ausnahmen zu behandeln, die im Code behandelt werden müssen.

Rahul Raina
quelle
Eine andere Möglichkeit besteht darin, das vorhandene Java-Objekt zu klonen (). Dadurch wird eine Kopie eines vorhandenen Java-Objekts erstellt. In diesem Fall müssen Sie auch das Deep-Copy- oder Shallow-Copy-Konzept behandeln.
Rahul Raina
2

Wenn jemand nach einer Möglichkeit sucht, eine Instanz einer Klasse zu erstellen, obwohl die Klasse dem Singleton-Muster folgt, finden Sie hier eine Möglichkeit, dies zu tun.

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

Dies funktioniert nur für Klassen, die Singleton-Muster mit einem privaten Konstruktor implementieren.

Omer
quelle
1

Sie können auch Methoden innerhalb des erstellten Objekts aufrufen.

Sie können ein Objekt sofort erstellen, indem Sie den ersten Konstruktor aufrufen und dann die erste Methode im erstellten Objekt aufrufen.

    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
quelle
Woher wissen Sie, dass der allererste Konstruktor Stringeinen Parameter verwendet? Wird ein wenig chaotisch, wenn Sie die Konstruktorreihenfolge ändern
Farid
@Farid aus der Klassendokumentation
Hatem Badawi
Ist getConstructor(ClassName.class)immer noch besser, denke ich. Selbst wenn sich die Reihenfolge der Konstruktoren in der Klasse ändert, muss die Position nicht manuell gefunden werden
Farid,
@Farid - c.getDeclaredMethods () [0] .invoke (Objekt, Objekt ... MethodArgs); Gibt in einigen Fällen einen speziellen Konstruktor an, den Sie möglicherweise benötigen, aber Sie haben Recht.
Hatem Badawi
1

Eine weitere hilfreiche Antwort. Wie verwende ich getConstructor (params) .newInstance (args)?

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

In meinem Fall verwendet der Konstruktor meiner Klasse den Webdriver als Parameter, der unter dem folgenden Code verwendet wird:

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