Contoh mencipta objek menggunakan Class.newInstance()

Bayangkan anda ditugaskan untuk mencipta objek menggunakan pantulan. Adakah kita mulakan?

Kami akan mulakan dengan menulis kod untuk kelas yang ingin kami nyatakan:


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

Ini akan menjadi kelas kami — dengan beberapa medan, pembina dengan parameter, getter dan setter, kaedah toString() dan blok permulaan. Sekarang mari kita beralih ke bahagian kedua: mencipta objek menggunakan pantulan. Pendekatan pertama yang akan kita lihat akan menggunakan 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());
    }
}

Cemerlang! Mari jalankan kod kami dan perhatikan umur yang akan dipaparkan. Tetapi kami mendapat ralat tentang pembina lalai yang hilang. Ternyata kaedah ini hanya membenarkan kita mendapatkan objek yang dibuat menggunakan pembina lalai. Mari tambahkan pembina lalai untuk kelas kami dan uji kod itu sekali lagi.

Mesej ralat:

Kod pembina baharu


public Employee() { }

Selepas menambah pembina, inilah outputnya:

umur ialah 1

Hebat! Kami mengetahui bagaimana kaedah ini berfungsi. Sekarang mari kita lihat di bawah tudung. Apabila membuka dokumentasi, kami melihat bahawa kaedah kami sudah tidak digunakan lagi :

Ia juga boleh membuang InstantiationException dan IllegalAccessException . Sehubungan itu, dokumentasi mencadangkan bahawa kita menggunakan cara lain untuk mencipta objek, iaitu Constructor.newInstance() . Mari analisa secara terperinci cara kelas Pembina berfungsi.

kaedah getConstructors dan getDeclaredConstructors

Untuk bekerja dengan kelas Pembina , kita perlu mendapatkan contoh terlebih dahulu. Kami mempunyai dua kaedah untuk ini: getConstructors dan getDeclaredConstructors .

Yang pertama mengembalikan tatasusunan pembina awam, dan yang kedua mengembalikan tatasusunan semua pembina kelas.

Mari kita berikan kelas kita sedikit privasi, atau lebih tepatnya, mari kita buat beberapa pembina peribadi untuk membantu menunjukkan cara kaedah ini berfungsi.

Mari tambahkan beberapa pembina peribadi:


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

Melihat kod, ambil perhatian bahawa salah satu pembina adalah peribadi:

Mari uji kaedah kami:


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

Dan kami mendapat keputusan ini:

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, ini adalah cara kita mendapat akses kepada objek Pembina . Sekarang kita boleh bercakap tentang apa yang boleh dilakukan.

Kelas java.lang.reflect.Constructor dan kaedah terpentingnya

Mari kita lihat kaedah yang paling penting dan cara ia berfungsi:

Kaedah Penerangan
getName() Mengembalikan nama pembina ini sebagai rentetan.
getModifiers() Mengembalikan pengubah suai akses Java yang dikodkan sebagai nombor.
getExceptionTypes() Mengembalikan tatasusunan objek Kelas yang mewakili jenis pengecualian yang diisytiharkan oleh pembina.
getParameters() Mengembalikan tatasusunan objek Parameter yang mewakili semua parameter. Mengembalikan tatasusunan panjang 0 jika pembina tidak mempunyai parameter.
getParameterTypes() Mengembalikan tatasusunan objek Kelas yang mewakili jenis parameter formal dalam susunan pengisytiharan.
getGenericParameterTypes() Mengembalikan tatasusunan objek Jenis yang mewakili jenis parameter formal dalam susunan pengisytiharan.

getName() & getModifiers()

Mari kita bungkus tatasusunan kami dalam Senarai untuk menjadikannya mudah untuk digunakan. Kami juga akan menulis kaedah getName dan 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;
}

Dan kaedah utama kami , di mana kami akan memanggil segala-galanya:


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

Dan kini kita melihat semua maklumat yang kita mahu:

Kelas pekerja:
Pembina :
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Pengubah suai :
[swasta, awam, awam]

getExceptionTypes()

Kaedah ini membolehkan kami mendapatkan pelbagai pengecualian yang mungkin dilemparkan oleh pembina kami. Mari ubah suai salah satu pembina kami dan tulis kaedah baharu.

Di sini kami menukar sedikit pembina semasa kami:


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

Dan di sini kita mempunyai kaedah untuk mendapatkan jenis pengecualian dan menambahnya ke main :


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

Di atas, kami mengakses pembina pertama dalam senarai kami. Kami akan membincangkan cara mendapatkan pembina tertentu sedikit kemudian.

Dan lihat output selepas kami menambah throws Exception :

Jenis pengecualian :
[class java.lang.Exception]

Dan sebelum menambah pengecualian:

Jenis pengecualian :
[]

Semuanya indah, tetapi bagaimana kita melihat parameter yang diperlukan oleh pembina kita? Mari kita fikirkan ini juga.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Mari kita mulakan semula dengan memperhalusi pembina peribadi kita. Sekarang ia akan kelihatan seperti ini:


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

Dan kami mempunyai tiga kaedah tambahan: getParameters untuk mendapatkan susunan parameter dan jenisnya, getParameterTypes untuk mendapatkan jenis parameter dan getGenericParameterTypes untuk mendapatkan jenis yang dibungkus dalam generik .


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

Dan kami menambah beberapa maklumat lagi kepada kaedah utama kami yang sudah tidak begitu kecil :


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

Melihat pada output, kami melihat maklumat yang sangat terperinci tentang parameter pembina kami:

Parameter pembina :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Jenis parameter :
[class java.lang.String, class java.lang.String, antara muka java.util.List]
Jenis parameter pembina :
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Ini jelas menunjukkan perbezaan antara setiap kaedah. Kami melihat bahawa kami mempunyai pilihan yang berasingan untuk mendapatkan maklumat tentang jenis parameter, jenis dibungkus dan segala-galanya secara umum. Super! Sekarang setelah kita mengenali kelas Pembina , kita boleh kembali ke topik utama artikel kami — mencipta objek.

Mencipta objek menggunakan Constructor.newInstance()

Cara kedua untuk mencipta objek ialah memanggil kaedah newInstance pada pembina. Mari kita lihat contoh yang berfungsi dan lihat bagaimana kita boleh mendapatkan pembina tertentu.

Jika anda ingin mendapatkan satu pembina, anda harus menggunakan kaedah getConstructor (jangan dikelirukan dengan getConstructors , yang mengembalikan tatasusunan semua pembina). Kaedah getConstructor mengembalikan pembina lalai.


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

Dan jika kita ingin mendapatkan pembina tertentu, kita perlu menghantar jenis parameter pembina kepada kaedah ini.

Jangan lupa bahawa kami hanya boleh mendapatkan pembina peribadi kami menggunakan kaedah getDeclaredConstructor .


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

Inilah cara kita boleh mendapatkan pembina tertentu. Sekarang mari kita cuba mencipta objek menggunakan pembina peribadi dan awam.

Pembina awam:


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

Hasilnya ialah objek yang boleh kita kerjakan:

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

Semuanya berfungsi hebat! Sekarang kita akan cuba dengan pembina peribadi:


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

Hasilnya ialah ralat tentang privasi pembina kami:

Java tidak dapat mencipta objek menggunakan pembina ini, tetapi sebenarnya ada sesuatu yang ajaib yang boleh kita lakukan dalam kaedah utama . Kami boleh mencapai tahap akses pembina kami, menjadikannya mungkin untuk mencipta objek kelas kami:


declaredConstructor.setAccessible(true);

Hasil mencipta objek

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

Kami tidak menetapkan umur dalam pembina kami, jadi ia kekal sama seperti semasa ia dimulakan.

Hebat, mari kita ringkaskan!

Faedah mencipta objek menggunakan Constructor.newInstance()

Kedua-dua kaedah berkongsi nama yang sama, tetapi terdapat perbezaan di antara mereka:

Class.newInstance() Constructor.newInstance()
Hanya boleh memanggil pembina no-arg . Boleh memanggil mana-mana pembina tanpa mengira bilangan parameter.
Memerlukan pembina untuk kelihatan. Boleh juga memanggil pembina persendirian dalam keadaan tertentu.
Membuang sebarang pengecualian (ditandai atau tidak) yang diisytiharkan oleh pembina. Sentiasa bungkus pengecualian yang dilemparkan dengan InvocationTargetException .

Atas sebab ini, Constructor.newInstance() diutamakan berbanding Class.newInstance() , dan merupakan kaedah yang digunakan oleh pelbagai rangka kerja dan API seperti Spring, Guava, Zookeeper, Jackson, Servlet, dsb.