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.

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").


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


81

Możesz użyć odbić

return Class.forName(className).getConstructor(String.class).newInstance(arg);

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];

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). ) .



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.


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.


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

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);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.