Tworzenie instancji za pomocą nazwy klasy i wywoływanie konstruktora

312

Czy istnieje sposób utworzenia instancji określonej klasy, biorąc pod uwagę nazwę klasy (dynamiczną) i przekazać parametry do jej konstruktora.

Coś jak:

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

Gdzie "MyAttributeValue"jest argument do konstruktora MyClass.

TheLameProgrammer
źródło

Odpowiedzi:

497

Tak, coś takiego:

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

Działa to oczywiście tylko dla jednego parametru ciągu, ale można go łatwo modyfikować.

Zauważ, że nazwa klasy musi być w pełni kwalifikowana, tzn. Zawierać przestrzeń nazw. W przypadku klas zagnieżdżonych musisz użyć dolara (ponieważ tego używa kompilator). Na przykład:

package foo;

public class Outer
{
    public static class Nested {}
}

Aby uzyskać do tego Classobiekt, potrzebujesz Class.forName("foo.Outer$Nested").

Jon Skeet
źródło
5
newInstance()jest metodą varargs (podobnie jak GetConstructor()), nie ma potrzeby Objecttworzenia jawnych tablic.
Joachim Sauer
18
@Jachachim: Wiem, że to varargs, ale ponieważ może być trudny, gdy masz Object[]argument, wolę utworzyć tablicę jawnie w tym przypadku.
Jon Skeet
2
clazz.getConstructor (String.class); dlaczego String.class tutaj?
Umair A.,
2
@Neutralizer: Tak, ale odpowiadałem na pytanie, które nie musi być dynamiczne.
Jon Skeet,
3
@JonSkeet Rozumiem skąd pochodzisz, jednak nie jest to takie proste - spojrzałem na dokumenty, ale byłem zdezorientowany, ale także jeśli przetestowałem i działało - ok, to działało - ale jeśli to nie działało, to Nie byłbym pewien, czy problem był spowodowany jakimś brakiem konfiguracji lub czymś z mojej strony - często zadając tak proste pytania, ludzie wrzucają przydatne ciekawostki, które naprawdę pomagają. Dlatego proste „tak, to by działało - jeśli zrobisz to w ten sposób” lub „nie, nie ma mowy”, naprawdę pomaga. Ale rozumiem teraz, że nie ma mowy
ycomp
97

Możesz użyć, Class.forName()aby uzyskaćClass obiekt pożądanej klasy.

Następnie użyj, getConstructor()aby znaleźć żądanyConstructor obiekt.

Na koniec wywołaj newInstance()ten obiekt, aby uzyskać nową instancję.

Class<?> c = Class.forName("mypackage.MyClass");
Constructor<?> cons = c.getConstructor(String.class);
Object object = cons.newInstance("MyAttributeValue");
Joachim Sauer
źródło
81

Możesz użyć odbić

return Class.forName(className).getConstructor(String.class).newInstance(arg);
Peter Lawrey
źródło
3
Jeśli używasz domyślnego konstruktora, usuń wartość parametru String.class, np. Return Class.forName (className) .getConstructor (). NewInstance (arg);
Vijay Kumar,
21
@VijayKumar Myślę, że masz na myśli Class.forName(className).getConstructor().newInstance();;)
Peter Lawrey
14

Jeśli klasa ma tylko jeden pusty konstruktor (np. Activity lub Fragment itp., Klasy Androida):

Class<?> myClass = Class.forName("com.example.MyClass");    
Constructor<?> constructor = myClass.getConstructors()[0];
poziom777
źródło
2
To mi pomogło. Constructor<?> ctor = clazz.getConstructor(String.class)chyba nie działało dla mnie.
Leo C Han,
8

przy użyciu (tj.) getConstructor(String.lang)konstruktor musi zostać zadeklarowany jako publiczny. W przeciwnym razie NoSuchMethodExceptionwyrzucane jest a .

jeśli chcesz uzyskać dostęp do niepublicznego konstruktora , musisz go użyć (tj getDeclaredConstructor(String.lang). ) .

matthiasboesinger
źródło
4

Bardzo prosty sposób na utworzenie obiektu w Javie przy użyciu Class<?>argumentów konstruktora:

Przypadek 1: - Tutaj jest mały kod w tej Mainklasie:

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

}

A oto Basestruktura klas:

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

}

Przypadek 2: - Możesz kodować podobnie dla konstruktora z wieloma argumentami i konstruktorem kopiowania. Na przykład przekazanie konstruktorowi 3 argumentów jako parametru Basebędzie wymagało utworzenia konstruktora w klasie i zmiany kodu powyżej:

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

I tutaj klasa podstawowa powinna jakoś wyglądać:

public class Base {

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

Uwaga: - Nie zapomnij obsługiwać różnych wyjątków, które muszą być obsługiwane w kodzie.

Rahul Raina
źródło
Innym sposobem jest również klonowanie () istniejącego obiektu Java. Spowoduje to utworzenie kopii istniejącego obiektu Java. W tym przypadku musisz także obsłużyć koncepcję kopiowania głębokiego lub płytkiego.
Rahul Raina
2

Jeśli ktoś szuka sposobu na utworzenie instancji klasy, mimo że klasa jest zgodna ze Wzorem Singletona, oto sposób na zrobienie tego.

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

Działa to tylko w przypadku klas, które implementują wzorzec singletonu za pomocą prywatnego konstruktora.

Omer
źródło
1

Możesz także wywoływać metody wewnątrz utworzonego obiektu.

Możesz utworzyć obiekt natychmiast, wywołując pierwszy konstruktor, a następnie wywołać pierwszą metodę w utworzonym obiekcie.

    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
źródło
Skąd będziesz wiedzieć, że pierwszy konstruktor przyjmuje Stringjako parametr? Staje się trochę niechlujny, kiedy zmieniasz kolejność konstruktorów
Farid
@Odkryty z dokumentacji zajęć
Hatem Badawi
getConstructor(ClassName.class)Myślę, że nadal jest lepiej. Nawet jeśli kolejność konstruktorów zmienia się w klasie, nie trzeba ręcznie szukać pozycji
Farid
@Farid - c.getDeclaredMethods () [0] .invoke (object, Object ... MethodArgs); określa specjalnego konstruktora, w niektórych przypadkach możesz tego potrzebować, ale masz rację.
Hatem Badawi
1

Kolejna pomocna odpowiedź. Jak korzystać z getConstructor (params) .newInstance (args)?

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

W moim przypadku konstruktor mojej klasy przyjmuje jako parametr Webdriver, więc użyłem go poniżej:

return Class.forName("com.page.BillablePage")
    .getConstructor(WebDriver.class)
    .newInstance(this.driver);
użytkownik3181500
źródło