Contoh membuat objek menggunakan Class.newInstance()

Bayangkan Anda ditugaskan untuk membuat objek menggunakan refleksi. Mari kita mulai?

Kita akan mulai dengan menulis kode untuk kelas yang ingin kita buat instance-nya:

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 kita — dengan beberapa bidang, konstruktor dengan parameter, pengambil dan penyetel, metode toString() , dan blok inisialisasi. Sekarang mari beralih ke bagian kedua: membuat objek menggunakan refleksi. 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());
    }
}

Bagus sekali! Mari jalankan kode kita dan perhatikan usia yang akan ditampilkan. Tapi kami mendapatkan kesalahan tentang konstruktor default yang hilang. Ternyata metode ini hanya memungkinkan kita membuat objek yang dibuat menggunakan konstruktor default. Mari tambahkan konstruktor default untuk kelas kita dan uji kodenya lagi.

Pesan eror:

Kode konstruktor baru

public Employee() { }

Setelah menambahkan konstruktor, inilah hasilnya:

usia adalah 1

Besar! Kami menemukan cara kerja metode ini. Sekarang mari kita lihat di bawah tenda. Membuka dokumentasi, kami melihat bahwa metode kami sudah usang :

Itu juga bisa melempar InstantiationException dan IllegalAccessException . Oleh karena itu, dokumentasi menyarankan agar kita menggunakan cara lain untuk membuat objek, yaitu Constructor.newInstance() . Mari kita analisis secara detail bagaimana kelas Konstruktor bekerja.

metode getConstructors dan getDeclaredConstructors

Untuk bekerja dengan kelas Constructor , pertama-tama kita perlu mendapatkan sebuah instance. Kami memiliki dua metode untuk ini: getConstructors dan getDeclaredConstructors .

Yang pertama mengembalikan larik konstruktor publik, dan yang kedua mengembalikan larik semua konstruktor kelas.

Mari berikan privasi pada kelas kita, atau lebih tepatnya, mari buat beberapa konstruktor pribadi untuk membantu mendemonstrasikan cara kerja metode ini.

Mari tambahkan beberapa konstruktor pribadi:

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

Melihat kodenya, perhatikan bahwa salah satu konstruktor bersifat pribadi:

Mari kita uji metode kita:

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 mendapatkan hasil 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)
publik com.codegym.Employee(java.lang.String,java.lang.String,int)
publik com.codegym.Employee()

Oke, beginilah cara kita mendapatkan akses ke objek Constructor . Sekarang kita dapat berbicara tentang apa yang dapat dilakukannya.

Kelas java.lang.reflect.Constructor dan metode terpentingnya

Mari kita lihat metode yang paling penting dan cara kerjanya:

metode Keterangan
getName() Mengembalikan nama konstruktor ini sebagai string.
getModifiers() Mengembalikan pengubah akses Java yang dikodekan sebagai angka.
getExceptionTypes() Mengembalikan larik objek Kelas yang mewakili jenis pengecualian yang dideklarasikan oleh konstruktor.
getParameters() Mengembalikan array objek Parameter yang mewakili semua parameter. Mengembalikan array dengan panjang 0 jika konstruktor tidak memiliki parameter.
getParameterTypes() Mengembalikan array objek Kelas yang mewakili tipe parameter formal dalam urutan deklarasi.
getGenericParameterTypes() Mengembalikan array objek Tipe yang mewakili tipe parameter formal dalam urutan deklarasi.

getName() & getModifiers()

Mari bungkus array kita dalam sebuah List agar mudah digunakan. Kami juga akan menulis metode 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 metode utama kami , di mana kami akan memanggil semuanya:

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 sekarang kami melihat semua informasi yang kami inginkan:

Kelas karyawan:
Konstruktor:
[com.codegym.Employee pribadi (java.lang.String), com.codegym.Employee publik
(java.lang.String, java.lang.String, int), com.codegym.Employee publik () ]
Pengubah:
[pribadi, publik, publik]

getExceptionTypes()

Metode ini memungkinkan kita mendapatkan larik pengecualian yang mungkin dilemparkan oleh konstruktor kita. Mari ubah salah satu konstruktor kita dan tulis metode baru.

Di sini kami sedikit mengubah konstruktor kami saat ini:

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

Dan di sini kami memiliki metode untuk mendapatkan jenis pengecualian dan menambahkannya 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 konstruktor pertama dalam daftar kami. Kami akan membahas cara mendapatkan konstruktor tertentu nanti.

Dan lihat hasilnya setelah kita menambahkan throws Exception :

Jenis pengecualian :
[kelas java.lang.Exception]

Dan sebelum menambahkan pengecualian:

Jenis pengecualian :
[]

Semuanya bagus, tapi bagaimana kita melihat parameter apa yang dibutuhkan oleh konstruktor kita? Mari kita cari tahu ini juga.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Mari kita mulai lagi dengan menyempurnakan konstruktor pribadi kita. Sekarang akan terlihat seperti ini:

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

Dan kami memiliki tiga metode tambahan: getParameters untuk mendapatkan urutan parameter dan tipenya, getParameterTypes untuk mendapatkan tipe parameter, dan getGenericParameterTypes untuk mendapatkan tipe yang dibungkus dengan 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()));
}

Dan kami menambahkan beberapa informasi lagi ke metode utama kami yang sudah tidak terlalu 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 outputnya, kami melihat informasi yang sangat detail tentang parameter konstruktor kami:

Parameter konstruktor :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Jenis parameter :
[kelas java.lang.String, kelas java.lang.String, antarmuka java.util.List]
Tipe parameter konstruktor :
[kelas java.lang.String, kelas java.lang.String, java.util.List<java.lang.String>]

Ini jelas menunjukkan perbedaan antara masing-masing metode. Kami melihat bahwa kami memiliki opsi terpisah untuk mendapatkan informasi tentang tipe parameter, tipe terbungkus, dan semuanya secara umum. Super! Sekarang setelah kita mengenal class Constructor , kita dapat kembali ke topik utama artikel kita — membuat objek.

Membuat objek menggunakan Constructor.newInstance()

Cara kedua untuk membuat objek adalah dengan memanggil metode newInstance pada konstruktor. Mari kita lihat contoh yang berfungsi dan lihat bagaimana kita bisa mendapatkan konstruktor tertentu.

Jika Anda ingin mendapatkan satu konstruktor, Anda harus menggunakan metode getConstructor (jangan bingung dengan getConstructors , yang mengembalikan array dari semua konstruktor). Metode getConstructor mengembalikan konstruktor default.

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

Dan jika kita ingin mendapatkan konstruktor tertentu, kita perlu meneruskan tipe parameter konstruktor ke metode ini.

Jangan lupa bahwa kita hanya bisa mendapatkan konstruktor pribadi kita menggunakan metode getDeclaredConstructor .

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

Ini adalah bagaimana kita bisa mendapatkan konstruktor tertentu. Sekarang mari kita coba membuat objek menggunakan konstruktor privat dan publik.

Konstruktor publik:

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 adalah objek yang dapat kita kerjakan:

public com.codegym.Employee(java.lang.String,java.lang.String,int)
Karyawan{name='Rob' nama belakang='Stark', umur=10}

Semuanya bekerja dengan baik! Sekarang kita akan mencoba dengan konstruktor pribadi:

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 adalah kesalahan tentang privasi konstruktor kami:

Java tidak dapat membuat objek menggunakan konstruktor ini, tetapi sebenarnya ada sesuatu yang ajaib yang dapat kita lakukan dalam metode utama . Kita dapat menggunakan tingkat akses konstruktor kita, sehingga memungkinkan untuk membuat objek kelas kita:

declaredConstructor.setAccessible(true);

Hasil membuat objek

pribadi com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Karyawan{name='Rob', nama belakang='Stark', umur=-1}

Kami tidak menyetel usia di konstruktor kami, sehingga tetap sama seperti saat diinisialisasi.

Luar biasa, mari kita rangkum!

Manfaat membuat objek menggunakan Constructor.newInstance()

Kedua metode memiliki nama yang sama, tetapi ada perbedaan di antara keduanya:

Kelas.newInstance() Konstruktor.newInstance()
Hanya dapat memanggil konstruktor tanpa arg . Dapat memanggil konstruktor apa pun terlepas dari jumlah parameter.
Membutuhkan konstruktor untuk terlihat. Dapat juga memanggil konstruktor pribadi dalam keadaan tertentu.
Melempar pengecualian apa pun (dicentang atau tidak) yang dideklarasikan oleh konstruktor. Selalu bungkus pengecualian yang dilemparkan dengan InvocationTargetException .

Karena alasan ini, Constructor.newInstance() lebih disukai daripada Class.newInstance() , dan merupakan metode yang digunakan oleh berbagai kerangka kerja dan API seperti Spring, Guava, Zookeeper, Jackson, Servlet, dll.