Class.newInstance() kullanarak bir nesne yaratma örneği

Yansımayı kullanarak bir nesne yaratmakla görevlendirildiğinizi hayal edin. Başlayalım mı?

Başlatmak istediğimiz sınıfın kodunu yazarak başlayacağız:


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

Bu, birkaç alan, parametreler, alıcılar ve ayarlayıcılar içeren bir yapıcı, bir toString() yöntemi ve bir başlatma bloğu ile bizim sınıfımız olacak . Şimdi ikinci kısma geçelim: yansımayı kullanarak nesne oluşturma. Bakacağımız ilk yaklaşım Class.newInstance()' ı kullanacak .


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

Harika! Kodumuzu çalıştıralım ve görüntülenecek yaşı izleyelim. Ancak eksik bir varsayılan kurucu hakkında bir hata alıyoruz. Bu yöntemin yalnızca varsayılan bir kurucu kullanılarak oluşturulmuş bir nesneyi elde etmemize izin verdiği ortaya çıktı. Sınıfımız için varsayılan bir oluşturucu ekleyelim ve kodu tekrar test edelim.

Hata mesajı:

Yeni yapıcının kodu


public Employee() { }

Yapıcıyı ekledikten sonra, işte çıktı:

yaş 1

Harika! Bu yöntemin nasıl çalıştığını anladık. Şimdi kaputun altına bir göz atalım. Belgeleri açarak, yöntemimizin zaten kullanımdan kaldırıldığını görüyoruz :

Ayrıca InstantiationException ve IllegalAccessException atabilir . Buna göre belgeler, bir nesne oluşturmanın diğer yolunu, yani Constructor.newInstance() kullandığımızı önerir . Constructor sınıfının nasıl çalıştığını ayrıntılı olarak inceleyelim .

getConstructors ve getDeclaredConstructors yöntemleri

Constructor sınıfıyla çalışmak için önce bir örnek almamız gerekiyor. Bunun için iki yöntemimiz var: getConstructors ve getDeclaredConstructors .

İlki, bir dizi genel oluşturucu döndürür ve ikincisi, tüm sınıf oluşturucularının bir dizisini döndürür.

Sınıfımıza biraz mahremiyet verelim veya daha doğrusu, bu yöntemlerin nasıl çalıştığını göstermeye yardımcı olacak bazı özel oluşturucular oluşturalım.

Bazı özel kurucular ekleyelim:


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

Koda bakıldığında, yapıcılardan birinin özel olduğuna dikkat edin:

Yöntemlerimizi test edelim:


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

Ve şu sonucu elde ederiz:

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

getDeclaredConstructors:
özel com.codegym.Employee(java.lang.String,java.lang .String)
genel com.codegym.Employee(java.lang.String,java.lang.String,int)
genel com.codegym.Employee()

Tamam, Constructor nesnesine bu şekilde erişiyoruz . Şimdi neler yapabileceği hakkında konuşabiliriz.

Java.lang.reflect.Constructor sınıfı ve en önemli yöntemleri

En önemli yöntemlere ve nasıl çalıştıklarına bir göz atalım:

Yöntem Tanım
getName() Bu yapıcının adını bir dize olarak döndürür.
getModifiers() Sayı olarak kodlanmış Java erişim değiştiricilerini döndürür.
getExceptionTypes() Yapıcı tarafından bildirilen özel durum türlerini temsil eden Class nesneleri dizisini döndürür.
getParameters() Tüm parametreleri temsil eden bir Parameter nesneleri dizisi döndürür . Yapıcının parametresi yoksa 0 uzunluğunda bir dizi döndürür.
getParameterTypes() Bildirim sırasına göre resmi parametre türlerini temsil eden Class nesnelerinin bir dizisini döndürür.
getGenericParameterTypes() Bildirim sırasına göre biçimsel parametre türlerini temsil eden Type nesnelerinin bir dizisini döndürür.

getName() & getModifiers()

Çalışmayı kolaylaştırmak için dizimizi bir Listeye saralım . Ayrıca getName ve getModifiers metotlarını da yazacağız :


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

Ve her şeyi arayacağımız ana yöntemimiz:


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

Ve şimdi istediğimiz tüm bilgileri görüyoruz:

Çalışan sınıfı:
Yapıcılar :
[özel com.codegym.Employee(java.lang.String), genel
com.codegym.Employee(java.lang.String,java.lang.String,int), genel com.codegym.Employee() ]
Değiştiriciler :
[özel, genel, genel]

getExceptionTypes()

Bu yöntem, yapıcımızın atabileceği istisnaların bir dizisini almamızı sağlar. Yapıcılarımızdan birini değiştirelim ve yeni bir yöntem yazalım.

Burada mevcut kurucumuzu biraz değiştiriyoruz:


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

Ve burada, istisna türlerini almak ve onu main'e eklemek için bir yöntemimiz var :


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

Yukarıda, listemizdeki ilk kurucuya eriştik. Belirli bir kurucuyu nasıl elde edeceğimizi biraz sonra tartışacağız.

Ve throws Exception ekledikten sonra çıktıya bakın :

İstisna türleri:
[class java.lang.Exception]

Ve istisnayı eklemeden önce:

İstisna türleri :
[]

Her şey harika, ancak yapıcılarımız için hangi parametrelerin gerekli olduğunu nasıl görebiliriz? Bunu da çözelim.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Özel kurucumuzu iyileştirerek yeniden başlayalım. Şimdi şöyle görünecek:


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

Ve üç ek yöntemimiz daha var: parametrelerin sırasını ve türlerini almak için getParameters , parametre türlerini almak için getParameterTypes ve türlere sarılmış türleri almak için getGenericParameterTypes jenerikler .


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

Ve zaten çok küçük olmayan ana yöntemimize biraz daha bilgi ekliyoruz :


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

Çıktıya baktığımızda, yapıcılarımızın parametreleri hakkında çok ayrıntılı bilgiler görüyoruz:

Yapıcı parametreleri :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Parametre türleri :
[class java.lang.String, class java.lang.String, arabirim java.util.List]
Yapıcı parametre türleri:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Bu, her bir yöntem arasındaki farkı açıkça göstermektedir. Parametre türleri, sarılmış türler ve genel olarak her şey hakkında bilgi almak için ayrı seçeneklerimiz olduğunu görüyoruz. Süper! Artık Constructor sınıfına aşina olduğumuza göre , makalemizin ana konusu olan nesne yaratmaya dönebiliriz.

Constructor.newInstance() kullanarak bir nesne oluşturma

Nesne oluşturmanın ikinci yolu , yapıcıda newInstance yöntemini çağırmaktır . Çalışan bir örneğe bakalım ve belirli bir kurucuyu nasıl elde edebileceğimizi görelim.

Tek bir kurucu elde etmek istiyorsanız, getConstructor yöntemini kullanmalısınız ( tüm kurucuların bir dizisini döndüren getConstructors ile karıştırılmamalıdır ). getConstructor yöntemi , varsayılan yapıcıyı döndürür.


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

Ve eğer belirli bir yapıcı elde etmek istiyorsak, yapıcının parametre türlerini bu yönteme geçirmemiz gerekir.

Özel kurucumuzu yalnızca getDeclaredConstructor yöntemini kullanarak elde edebileceğimizi unutmayın .


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

Belirli bir kurucuyu bu şekilde elde edebiliriz. Şimdi özel ve genel kurucuları kullanarak nesneler yaratmaya çalışalım.

Genel inşaatçı:


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

Sonuç, birlikte çalışabileceğimiz bir nesnedir:

public com.codegym.Employee(java.lang.String,java.lang.String,int)
Çalışan{name='Rob' surname='Stark', age=10}

Her şey harika çalışıyor! Şimdi özel bir kurucu ile deneyeceğiz:


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

Sonuç, oluşturucumuzun gizliliğiyle ilgili bir hatadır:

Java, bu yapıcıyı kullanarak bir nesne yaratamadı, ama aslında ana yöntemde yapabileceğimiz sihirli bir şey var . Kurucumuzun erişim seviyesini, sınıfımızın nesnelerini yaratmayı mümkün kılabiliriz:


declaredConstructor.setAccessible(true);

Bir nesne oluşturmanın sonucu

özel com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Çalışan{name='Rob', surname='Stark', age=-1}

Yapıcımızda yaş belirlemeyiz, bu nedenle ilk başlatıldığı zamankiyle aynı kalır.

Harika, özetleyelim!

Constructor.newInstance() kullanarak nesne oluşturmanın faydaları

Her iki yöntem de aynı adı paylaşır, ancak aralarında farklılıklar vardır:

Class.newInstance() Constructor.newInstance()
Yalnızca arg içermeyen bir oluşturucu çağırabilir . Parametre sayısından bağımsız olarak herhangi bir yapıcıyı çağırabilir.
Yapıcının görünür olmasını gerektirir. Belirli koşullar altında özel kurucuları da çağırabilir.
Yapıcı tarafından bildirilen herhangi bir özel durumu (işaretli veya işaretsiz) fırlatır. Atılan bir özel durumu her zaman InvocationTargetException ile sarar .

Bu nedenlerden dolayı, Class.newInstance () yerine Constructor.newInstance() tercih edilir ve Spring, Guava, Zookeeper, Jackson, Servlet vb. çeşitli çerçeveler ve API'ler tarafından kullanılan yöntemdir.