Halimbawa ng paglikha ng isang bagay gamit ang Class.newInstance()

Isipin na ikaw ay itinalaga upang lumikha ng isang bagay gamit ang pagmuni-muni. Magsisimula na ba tayo?

Magsisimula tayo sa pamamagitan ng pagsulat ng code para sa klase na gusto nating i-instantiate:


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 +
                '}';
    }
}

Ito ang magiging klase natin — na may maraming field, isang constructor na may mga parameter, getter at setter, isang toString() na paraan, at isang initialization block. Ngayon ay lumipat tayo sa pangalawang bahagi: paglikha ng isang bagay gamit ang pagmuni-muni. Ang unang diskarte na titingnan natin ay gagamit ng Class.newInstance() .


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

Magaling! Patakbuhin natin ang ating code at bantayan ang edad na ipapakita. Ngunit nakakakuha kami ng isang error tungkol sa isang nawawalang default na konstruktor. Lumalabas na ang pamamaraang ito ay nagbibigay-daan lamang sa amin na gumawa ng isang bagay gamit ang isang default na tagabuo. Magdagdag tayo ng default na constructor para sa ating klase at subukang muli ang code.

Maling mensahe:

Code ng bagong constructor


public Employee() { }

Pagkatapos idagdag ang constructor, narito ang output:

edad ay 1

Malaki! Nalaman namin kung paano gumagana ang pamamaraang ito. Ngayon tingnan natin sa ilalim ng hood. Ang pag-crack sa pagbukas ng dokumentasyon, nakita namin na ang aming pamamaraan ay hindi na ginagamit :

Maaari din nitong ihagis ang InstantiationException at IllegalAccessException . Alinsunod dito, iminumungkahi ng dokumentasyon na ginagamit namin ang ibang paraan ng paglikha ng isang bagay, katulad ng Constructor.newInstance() . Suriin natin nang detalyado kung paano gumagana ang klase ng Constructor .

getConstructors at getDeclaredConstructors pamamaraan

Upang gumana sa klase ng Constructor , kailangan muna nating kumuha ng isang instance. Mayroon kaming dalawang pamamaraan para dito: getConstructors at getDeclaredConstructors .

Ang una ay nagbabalik ng hanay ng mga pampublikong konstruktor, at ang pangalawa ay nagbabalik ng hanay ng lahat ng mga konstruktor ng klase.

Bigyan natin ng privacy ang ating klase, o sa halip, gumawa tayo ng ilang pribadong constructor para makatulong na ipakita kung paano gumagana ang mga pamamaraang ito.

Magdagdag tayo ng ilang pribadong constructor:


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

Sa pagtingin sa code, tandaan na ang isa sa mga konstruktor ay pribado:

Subukan natin ang aming mga pamamaraan:


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

At nakuha namin ang resultang ito:

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

Okay, ito ay kung paano tayo makakakuha ng access sa Constructor object. Ngayon ay maaari nating pag-usapan kung ano ang magagawa nito.

Ang klase ng java.lang.reflect.Constructor at ang pinakamahalagang pamamaraan nito

Tingnan natin ang pinakamahalagang pamamaraan at kung paano gumagana ang mga ito:

Pamamaraan Paglalarawan
getName() Ibinabalik ang pangalan ng constructor na ito bilang isang string.
getModifiers() Ibinabalik ang mga modifier ng access sa Java na naka-encode bilang isang numero.
getExceptionTypes() Nagbabalik ng hanay ng mga bagay sa Class na kumakatawan sa mga uri ng mga eksepsiyon na idineklara ng constructor.
getParameters() Nagbabalik ng hanay ng mga bagay na Parameter na kumakatawan sa lahat ng mga parameter. Ibinabalik ang isang array ng haba 0 kung ang constructor ay walang mga parameter.
getParameterTypes() Nagbabalik ng hanay ng mga object ng Class na kumakatawan sa mga pormal na uri ng parameter sa pagkakasunud-sunod ng deklarasyon.
getGenericParameterTypes() Nagbabalik ng hanay ng mga bagay na Uri na kumakatawan sa mga pormal na uri ng parameter sa pagkakasunud-sunod ng deklarasyon.

getName() at getModifiers()

I-wrap natin ang aming array sa isang Listahan para maging maginhawang gamitin. Magsusulat din kami ng mga pamamaraan ng getName at getModifiers :


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

At ang aming pangunahing pamamaraan, kung saan tatawagin namin ang lahat:


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

At ngayon nakikita namin ang lahat ng impormasyong gusto namin:

Employee class:
Constructors :
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Mga Modifier :
[pribado, pampubliko, pampubliko]

getExceptionTypes()

Ang pamamaraang ito ay nagbibigay-daan sa amin na makakuha ng isang hanay ng mga pagbubukod na maaaring itapon ng aming tagabuo. Baguhin natin ang isa sa ating mga constructor at magsulat ng bagong paraan.

Dito binago namin nang bahagya ang aming kasalukuyang constructor:


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

At narito mayroon kaming isang paraan para sa pagkuha ng mga uri ng pagbubukod at idagdag ito sa pangunahing :


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

Sa itaas, na-access namin ang unang constructor sa aming listahan. Tatalakayin natin kung paano makakuha ng isang partikular na constructor sa ibang pagkakataon.

At tingnan ang output pagkatapos naming magdagdag ng mga throws Exception :

Mga uri ng pagbubukod :
[class java.lang.Exception]

At bago idagdag ang pagbubukod:

Mga uri ng pagbubukod:
[]

Ang lahat ay kahanga-hanga, ngunit paano natin nakikita kung anong mga parameter ang kinakailangan ng ating mga konstruktor? Alamin din natin ito.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Magsimula tayong muli sa pamamagitan ng pagpino sa ating pribadong constructor. Ngayon magiging ganito ang hitsura:


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

At mayroon kaming tatlong karagdagang pamamaraan: getParameters para sa pagkuha ng pagkakasunud-sunod ng mga parameter at kanilang mga uri, getParameterTypes para sa pagkuha ng mga uri ng parameter, at getGenericParameterTypes para sa pagkuha ng mga uri na nakabalot sa generics .


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

At nagdaragdag kami ng ilang karagdagang impormasyon sa aming hindi pa gaanong maliit na pangunahing pamamaraan:


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

Sa pagtingin sa output, nakikita namin ang napaka detalyadong impormasyon tungkol sa mga parameter ng aming mga konstruktor:

Mga parameter ng konstruktor :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Mga uri ng parameter :
[class java.lang.String, class java.lang.String, interface java.util.List]
Mga uri ng parameter ng tagapagbuo :
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Ito ay malinaw na nagpapakita ng pagkakaiba sa pagitan ng bawat pamamaraan. Nakikita namin na mayroon kaming hiwalay na mga opsyon upang makakuha ng impormasyon sa mga uri ng parameter, mga uri ng balot, at lahat sa pangkalahatan. Super! Ngayong kilala na natin ang klase ng Constructor , maaari tayong bumalik sa pangunahing paksa ng ating artikulo — ang paglikha ng mga bagay.

Paglikha ng isang bagay gamit ang Constructor.newInstance()

Ang pangalawang paraan upang lumikha ng mga bagay ay ang pagtawag sa newInstance na paraan sa constructor. Tingnan natin ang isang gumaganang halimbawa at tingnan kung paano tayo makakakuha ng isang partikular na constructor.

Kung gusto mong makakuha ng iisang constructor, dapat mong gamitin ang getConstructor method (huwag malito getConstructors , na nagbabalik ng array ng lahat ng constructor). Ang getConstructor method ay nagbabalik ng default na constructor.


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

At kung gusto nating makakuha ng partikular na constructor, kailangan nating ipasa ang mga uri ng parameter ng constructor sa paraang ito.

Huwag kalimutan na makukuha lang natin ang ating pribadong constructor gamit ang getDeclaredConstructor method.


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

Ito ay kung paano tayo makakakuha ng isang partikular na tagabuo. Ngayon subukan nating lumikha ng mga bagay gamit ang pribado at pampublikong mga konstruktor.

Pampublikong tagabuo:


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

Ang resulta ay isang bagay na maaari nating gamitin:

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

Lahat ay gumagana nang mahusay! Ngayon ay susubukan namin sa isang pribadong tagabuo:


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

Ang resulta ay isang error tungkol sa privacy ng aming constructor:

Hindi makalikha ang Java ng isang bagay gamit ang constructor na ito, ngunit mayroon talagang isang mahiwagang magagawa natin sa pangunahing pamamaraan. Magagawa namin ang antas ng pag-access ng aming tagapagbuo, na ginagawang posible na lumikha ng mga bagay ng aming klase:


declaredConstructor.setAccessible(true);

Resulta ng paglikha ng isang bagay

pribadong com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Empleyado{name='Rob', surname='Stark', age=-1}

Hindi namin itinatakda ang edad sa aming constructor, kaya nananatili itong pareho noong nasimulan ito.

Kahanga-hanga, buod tayo!

Mga pakinabang ng paglikha ng mga bagay gamit ang Constructor.newInstance()

Ang parehong mga pamamaraan ay may parehong pangalan, ngunit may mga pagkakaiba sa pagitan nila:

Class.newInstance() Constructor.newInstance()
Makakatawag lang ng no-arg constructor. Maaaring tumawag sa anumang constructor anuman ang bilang ng mga parameter.
Nangangailangan ng constructor na makita. Maaari ding tumawag sa mga pribadong konstruktor sa ilalim ng ilang mga pangyayari.
Nagtapon ng anumang pagbubukod (nasuri o hindi) na idineklara ng tagabuo. Palaging binabalot ang isang itinapon na exception gamit ang InvocationTargetException .

Para sa mga kadahilanang ito, ang Constructor.newInstance() ay mas gusto kaysa sa Class.newInstance() , at ito ang paraan na ginagamit ng iba't ibang mga framework at API gaya ng Spring, Guava, Zookeeper, Jackson, Servlet, atbp.