Példa objektum létrehozására a Class.newInstance() használatával

Képzelje el, hogy Ön hozzá van rendelve egy objektum létrehozásához tükrözés segítségével. Kezdjük?

Kezdjük azzal, hogy megírjuk a példányosítani kívánt osztály kódját:


public class Employee {
    private String name;
    private String lastName;
    private int age;

    {
        age = -1;
        name = "Rob";
        surname = "Stark";
    }

    public Employee(String name, String surname, int age) {
        this.name = name;
        this.surname = surname;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return lastName;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                ", age=" + age +
                '}';
    }
}

Ez lesz a mi osztályunk – több mezővel, egy konstruktorral, paraméterekkel, getterekkel és setterekkel, egy toString() metódussal és egy inicializálási blokkkal. Most térjünk át a második részre: objektum létrehozása reflexióval. Az első megközelítés, amelyet megvizsgálunk, a Class.newInstance() függvényt fogja használni .


public class Main {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Employee employee = Employee.class.newInstance();
        System.out.println("age is " + employee.getAge());
    }
}

Kiváló! Futtassuk a kódunkat, és figyeljük, hogy megjelenik-e az életkor. De hibaüzenetet kapunk egy hiányzó alapértelmezett konstruktorról. Kiderült, hogy ezzel a módszerrel csak egy alapértelmezett konstruktor segítségével hozunk létre objektumot. Adjunk hozzá egy alapértelmezett konstruktort az osztályunkhoz, és teszteljük újra a kódot.

Hiba üzenet:

Az új kivitelező kódja


public Employee() { }

A konstruktor hozzáadása után itt a kimenet:

életkora 1

Nagy! Rájöttünk, hogyan működik ez a módszer. Most vessünk egy pillantást a motorháztető alá. A dokumentáció feltörésével azt látjuk, hogy a módszerünk már elavult :

Az InstantiationException és az IllegalAccessException parancsokat is el tudja dobni . Ennek megfelelően a dokumentáció azt javasolja, hogy az objektum létrehozásának másik módját használjuk, nevezetesen a Constructor.newInstance() . Elemezzük részletesen a Constructor osztály működését.

getConstructors és getDeclaredConstructors metódusok

A Constructor osztállyal való munkához először be kell szereznünk egy példányt. Két módszerünk van erre: getConstructors és getDeclaredConstructors .

Az első nyilvános konstruktorok tömbjét adja vissza, a második pedig az összes osztálykonstruktor tömbjét.

Adjunk egy kis magánéletet osztályunknak, vagy inkább hozzunk létre néhány privát konstruktort, amelyek segítenek bemutatni, hogyan működnek ezek a módszerek.

Adjunk hozzá néhány magánépítőt:


private Employee(String name, String surname) {
    this.name = name;
    this.lastName = lastName;
}

A kódot nézve vegye figyelembe, hogy az egyik konstruktor privát:

Teszteljük a módszereinket:


public class Main {
	  public static void main(String[] args) {
	      Class employeeClass = Employee.class;
	
	      System.out.println("getConstructors:");
	      printAllConstructors(employeeClass);
	
	      System.out.println("\n" +"getDeclaredConstructors:");
	      printDeclaredConstructors(employeeClass);
	  }
	
	  static void printDeclaredConstructors(Class<?> c){
	      for (Constructor<?> constructor : c.getDeclaredConstructors()) {
	          System.out.println(constructor);
	      }
	  }
	
	  static void printAllConstructors(Class<?> c){
	      for (Constructor<?> constructor : c.getConstructors()) {
	          System.out.println(constructor);
	      }
	  }
}

És ezt az eredményt kapjuk:

getConstructors:
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public.com.codegym.Employee()

getDeclaredConstructors:
private com.codegym.Employee(java.lang.String,java.lang .String)
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public com.codegym.Employee()

Oké, így jutunk hozzá a Constructor objektumhoz. Most beszélhetünk arról, hogy mire képes.

A java.lang.reflect.Constructor osztály és legfontosabb metódusai

Nézzük meg a legfontosabb módszereket és azok működését:

Módszer Leírás
getName() Ennek a konstruktornak a nevét adja vissza karakterláncként.
getModifiers() Visszaadja a Java hozzáférés módosítókat számként kódolva.
getExceptionTypes() Osztály objektumok tömbjét adja vissza, amelyek a konstruktor által deklarált kivételtípusokat képviselik.
getParameters() Az összes paramétert reprezentáló Parameter objektumok tömbjét adja vissza . 0 hosszúságú tömböt ad vissza, ha a konstruktornak nincsenek paraméterei.
getParameterTypes() Osztály objektumok tömbjét adja vissza, amelyek deklarációs sorrendben reprezentálják a formális paramétertípusokat.
getGenericParameterTypes() A formális paramétertípusokat deklarációs sorrendben reprezentáló Type objektumok tömbjét adja vissza.

getName() és getModifiers()

Tekerjük a tömbünket egy listába , hogy kényelmesebb legyen vele dolgozni. A getName és a getModifiers metódusokat is írjuk :


static List<Constructor<?>> getAllConstructors(Class<?> c) {
    return new ArrayList<>(Arrays.asList(c.getDeclaredConstructors()));
}

static List<String> getConstructorNames(List<Constructor<?>> constructors) {
    List<String> result = new ArrayList<>();
    for (Constructor<?> constructor : constructors) {
        result.add(constructor.toString());
    }
    return result;
}

static List<String> getConstructorModifiers(List<Constructor<?>> constructors) {
    List<String> result = new ArrayList<>();
    for (Constructor<?> constructor : constructors) {
        result.add(Modifier.toString(constructor.getModifiers()));
    }
    return result;
}

És a fő módszerünk, ahol mindent nevezünk:


public static void main(String[] args) {
    Class employeeClass = Employee.class;
    var constructors = getAllConstructors(employeeClass);
    var constructorNames = getConstructorNames(constructors);
    var constructorModifiers = getConstructorModifiers(constructors);

    System.out.println("Employee class:");
    System.out.println("Constructors :");
    System.out.println(constructorNames);
    System.out.println("Modifiers :");
    System.out.println(constructorModifiers);
}

És most minden szükséges információt látunk:

Munkavállalói osztály:
Konstruktorok :
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Módosítók :
[privát, nyilvános, nyilvános]

getExceptionTypes()

Ez a módszer lehetővé teszi, hogy megkapjuk a konstruktorunk által esetlegesen fellépő kivételek tömbjét. Módosítsuk az egyik konstruktorunkat, és írjunk egy új metódust.

Itt kicsit megváltoztatjuk jelenlegi konstruktorunkat:


private Employee(String name, String surname) throws Exception {
    this.name = name;
    this.lastName = lastName;
}

És itt van egy módszer a kivételtípusok lekérésére, és hozzáadjuk a főhöz :


static List<Class<?>> getConstructorExceptionTypes(Constructor<?> c) {
      return new ArrayList<>(Arrays.asList(c.getExceptionTypes()));
}


var constructorExceptionTypes = getConstructorExceptionTypes(constructors.get(0));
System.out.println("Exception types :");
System.out.println(constructorExceptionTypes);

Fent elértük a listánk első konstruktorát. Kicsit később megbeszéljük, hogyan szerezzünk be egy konkrét konstruktőrt.

És nézze meg a kimenetet, miután hozzáadtuk a kivételeket :

Kivételtípusok :
[class java.lang.Exception]

És mielőtt hozzáadná a kivételt:

Kivételtípusok:
[]

Minden csodálatos, de hogyan látjuk, milyen paramétereket követelnek meg a kivitelezőink? Találjuk ki ezt is.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Kezdjük újra a privát kivitelezőnk finomításával. Most így fog kinézni:


private Employee(String name, String surname, List<String> list) {
    this.name = name;
    this.lastName = lastName;
}

Három további módszerünk van: getParameters a paraméterek és típusaik sorrendjének lekéréséhez, getParameterTypes a paramétertípusok lekéréséhez, és getGenericParameterTypes a típusok generikusba csomagolt lekéréséhez .


static List<Parameter> getConstructorParameters(Constructor<?> c) {
    return new ArrayList<>(Arrays.asList(c.getParameters()));
}

static List<Class<?>> getConstructorParameterTypes(Constructor<?> c) {
    return new ArrayList<>(Arrays.asList(c.getParameterTypes()));
}

static List<Type> getConstructorParametersGenerics(Constructor<?> c) {
    return new ArrayList<>(Arrays.asList(c.getGenericParameterTypes()));
}

És adunk még néhány információt a már nem is olyan kicsi módszerünkhöz:


var constructorParameterTypes = getConstructorParameterTypes(constructors.get(0));
var constructorParameters = getConstructorParameters(constructors.get(0));
var constructorParametersGenerics = getConstructorParametersGenerics(constructors.get(0));

System.out.println("Constructor parameters :");
System.out.println(constructorParameters);

System.out.println("Parameter types :");
System.out.println(constructorParameterTypes);

System.out.println("Constructor parameter types :");
System.out.println(constructorParametersGenerics);

A kimenetet nézve nagyon részletes információkat látunk a konstruktoraink paramétereiről:

Konstruktor paraméterei :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Paramétertípusok :
[class java.lang.String, class java.lang.String, interfész java.util.List]
Konstruktor paramétertípusai :
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Ez egyértelműen mutatja az egyes módszerek közötti különbséget. Azt látjuk, hogy külön lehetőségeink vannak a paramétertípusokról, a becsomagolt típusokról és általában mindenről információk beszerzésére. Szuper! Most, hogy megismertük a Constructor osztályt, visszatérhetünk cikkünk fő témájához – az objektumok létrehozásához.

Objektum létrehozása a Constructor.newInstance() segítségével

Az objektumok létrehozásának második módja a newInstance metódus meghívása a konstruktoron. Nézzünk meg egy működő példát, és nézzük meg, hogyan szerezhetünk be egy adott konstruktort.

Ha egyetlen konstruktort szeretne beszerezni, használja a getConstructor metódust (nem tévesztendő össze a getConstructors-szal , amely az összes konstruktor tömbjét adja vissza). A getConstructor metódus az alapértelmezett konstruktort adja vissza.


public static void main(String[] args) throws NoSuchMethodException {
    Class employeeClass = Employee.class;
    Constructor<?> employeeConstructor = employeeClass.getConstructor();
    System.out.println(employeeConstructor);
}
public com.codegym.Employee()

Ha pedig konkrét konstruktort akarunk kapni, akkor ennek a metódusnak át kell adnunk a konstruktor paramétertípusait.

Ne felejtsük el, hogy privát konstruktorunkat csak a getDeclaredConstructor metódussal tudjuk megszerezni .


Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);

Így kaphatunk egy konkrét konstruktort. Most próbáljunk meg objektumokat létrehozni privát és nyilvános konstruktorok használatával.

Nyilvános kivitelező:


Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor(String.class, String.class, int.class);
System.out.println(employeeConstructor);

Employee newInstance = (Employee) employeeConstructor.newInstance("Rob", "Stark", 10);
System.out.println(newInstance);

Az eredmény egy objektum, amellyel dolgozhatunk:

public com.codegym.Employee(java.lang.String,java.lang.String,int)
Alkalmazott{name='Rob' surname='Stark', age=10}

Minden remekül működik! Most megpróbáljuk egy privát kivitelezővel:


Constructor<?> declaredConstructor = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(declaredConstructor);

Employee newInstance2 = (Employee) declaredConstructor.newInstance("Rob", "Stark", new ArrayList<>());
System.out.printf(newInstance2.toString());

Az eredmény egy hiba a konstruktorunk adatvédelmével kapcsolatban:

A Java nem tudott objektumot létrehozni ezzel a konstruktorral, de valójában van valami varázslatos, amit tehetünk a metódusban. A konstruktorunk hozzáférési szintjét tudjuk elérni, lehetővé téve osztályunk objektumainak létrehozását:


declaredConstructor.setAccessible(true);

Egy objektum létrehozásának eredménye

private com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Alkalmazott{name='Rob', vezetéknév='Stark', age=-1}

Nem állítjuk be az életkort a konstruktorunkban, így az ugyanaz marad, mint az inicializáláskor.

Csodálatos, összegezzük!

Az objektumok Constructor.newInstance() használatával történő létrehozásának előnyei

Mindkét módszernek ugyanaz a neve, de vannak különbségek közöttük:

Class.newInstance() Constructor.newInstance()
Csak no-arg konstruktort lehet hívni . Bármilyen konstruktort hívhat, függetlenül a paraméterek számától.
Megköveteli, hogy a konstruktor látható legyen. Bizonyos körülmények között magánépítőket is hívhat.
Bármilyen kivételt dob ​​(ellenőrzött vagy nem), amelyet a konstruktor deklarált. A kidobott kivételeket mindig az InvocationTargetException elemre csomagolja .

Ezen okok miatt a Constructor.newInstance() előnyben részesített a Class.newInstance() helyett , és ezt a módszert használják különböző keretrendszerek és API-k, például Spring, Guava, Zookeeper, Jackson, Servlet stb.