Пример за създаване на обект с помощта на Class.newInstance()
Представете си, че ви е възложено да създадете обект с помощта на отражение. Да започваме ли?
Ще започнем с написването на codeа за класа, който искаме да създадем:
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 +
'}';
}
}
Това ще бъде нашият клас — с няколко полета, конструктор с параметри, гетери и сетери, метод toString() и блок за инициализация. Сега нека преминем към втората част: създаване на обект с помощта на отражение. Първият подход, който ще разгледаме, ще използва 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());
}
}
Отлично! Нека изпълним нашия code и да гледаме възрастта да се покаже. Но получаваме грешка за липсващ конструктор по подразбиране. Оказва се, че този метод ни позволява само да получим обект, създаден с помощта на конструктор по подразбиране. Нека добавим конструктор по подразбиране за нашия клас и отново да тестваме codeа.
Съобщение за грешка:
![](https://cdn.codegym.cc/images/article/217f2884-4ba7-4884-a7be-08d837c2eaec/1080.jpeg)
Код на новия конструктор
public Employee() { }
След добавяне на конструктора, ето изхода:
Страхотен! Разбрахме How работи този метод. Сега нека надникнем под капака. Отваряйки documentацията, виждаме, че нашият метод вече е отхвърлен :
![](https://cdn.codegym.cc/images/article/33c0cd23-1278-4ef2-8988-3250596cf390/1080.jpeg)
Може също да хвърля InstantiationException и IllegalAccessException . Съответно, documentацията предлага да използваме другия начин за създаване на обект, а именно Constructor.newInstance() . Нека анализираме подробно How работи класът Constructor .
методи getConstructors и getDeclaredConstructors
За да работим с класа Constructor , първо трябва да получим екземпляр. Имаме два метода за това: getConstructors и getDeclaredConstructors .
Първият връща масив от публични конструктори, а вторият връща масив от всички конструктори на класове.
Нека да дадем малко поверителност на нашия клас, or по-скоро, нека създадем частни конструктори, които да ни помогнат да демонстрираме How работят тези методи.
Нека добавим някои частни конструктори:
private Employee(String name, String surname) {
this.name = name;
this.lastName = lastName;
}
Разглеждайки codeа, имайте предвид, че един от конструкторите е частен:
![](https://cdn.codegym.cc/images/article/006e7a94-70f3-494a-a86b-230b326b57e6/512.jpeg)
Нека тестваме нашите методи:
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);
}
}
}
И получаваме този резултат:
публичен com.codegym.Employee(java.lang.String,java.lang.String,int)
public.com.codegym.Employee()
getDeclaredConstructors:
частен com.codegym.Employee(java.lang.String,java.lang .String)
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public com.codegym.Employee()
Добре, така получаваме достъп до обекта Constructor . Сега можем да говорим за това Howво може да направи.
Класът java.lang.reflect.Constructor и неговите най-важни методи
Нека да разгледаме най-важните методи и How работят:
Метод | Описание |
---|---|
getName() | Връща името на този конструктор като низ. |
getModifiers() | Връща модификаторите за достъп на Java, codeирани като число. |
getExceptionTypes() | Връща масив от Class обекти, които представляват типовете изключения, декларирани от конструктора. |
getParameters() | Връща масив от обекти Parameter , представящи всички параметри. Връща масив с дължина 0, ако конструкторът няма параметри. |
getParameterTypes() | Връща масив от Class обекти, които представляват формалните типове параметри в реда на деклариране. |
getGenericParameterTypes() | Връща масив от обекти тип, които представляват формалните типове параметри в реда на деклариране. |
getName() & getModifiers()
Нека увием нашия масив в списък , за да го направим удобен за работа. Ще напишем също методи getName и 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;
}
И основният ни метод, където ще извикаме всичко:
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);
}
И сега виждаме цялата информация, която искаме:
Конструктори:
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Модификатори:
[частен, публичен, обществен]
getExceptionTypes()
Този метод ни позволява да получим масив от изключения, които нашият конструктор може да хвърли. Нека модифицираме един от нашите конструктори и да напишем нов метод.
Тук променяме леко текущия ни конструктор:
private Employee(String name, String surname) throws Exception {
this.name = name;
this.lastName = lastName;
}
И тук имаме метод за получаване на типове изключения и добавянето му към 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);
По-горе получихме достъп до първия конструктор в нашия списък. Ще обсъдим How да получите конкретен конструктор малко по-късно.
И вижте изхода, след като добавим throws Exception :
[клас java.lang.Exception]
И преди да добавите изключението:
[]
Всичко е прекрасно, но How да видим Howви параметри изискват нашите конструктори? Нека да разберем и това.
getParameters() & getParameterTypes() & getGenericParameterTypes()
Нека започнем отново, като прецизираме нашия частен конструктор. Сега ще изглежда така:
private Employee(String name, String surname, List<String> list) {
this.name = name;
this.lastName = lastName;
}
Имаме и три допълнителни метода: getParameters за получаване на реда на параметрите и техните типове, getParameterTypes за получаване на типовете параметри и getGenericParameterTypes за получаване на типове, обвити в генерични .
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()));
}
И ние добавяме още малко информация към нашия вече не толкова малък основен метод:
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);
Разглеждайки изхода, виждаме много подробна информация за параметрите на нашите конструктори:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Типове параметри:
[клас java.lang.String, клас java.lang.String, интерфейс java.util.List]
Типове параметри на конструктора:
[клас java.lang.String, клас java.lang.String, java.util.List<java.lang.String>]
Това ясно показва разликата между всеки метод. Виждаме, че имаме отделни опции за получаване на информация за типове параметри, обвити типове и всичко като цяло. Супер! Сега, след като се запознахме с класа Constructor , можем да се върнем към основната тема на нашата статия - създаване на обекти.
Създаване на обект с помощта на Constructor.newInstance()
Вторият начин за създаване на обекти е да се извика методът newInstance на конструктора. Нека да разгледаме работещ пример и да видим How можем да получим определен конструктор.
Ако искате да получите един конструктор, трябва да използвате метода getConstructor (да не се бърка с getConstructors , който връща масив от всички конструктори). Методът getConstructor връща конструктора по подразбиране.
public static void main(String[] args) throws NoSuchMethodException {
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor();
System.out.println(employeeConstructor);
}
И ако искаме да получим конкретен конструктор, трябва да предадем типовете параметри на конструктора към този метод.
Не забравяйте, че можем да получим нашия частен конструктор само чрез метода getDeclaredConstructor .
Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);
Така можем да получим конкретен конструктор. Сега нека се опитаме да създадем обекти, използвайки частни и публични конструктори.
Публичен конструктор:
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);
Резултатът е обект, с който можем да работим:
Employee{name='Rob' surname='Stark', age=10}
Всичко работи отлично! Сега ще опитаме с частен конструктор:
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());
Резултатът е грешка относно поверителността на нашия конструктор:
![](https://cdn.codegym.cc/images/article/2146ce5a-dbdc-4925-8904-af52a946958e/1080.jpeg)
Java не можа да създаде обект с помощта на този конструктор, но всъщност има нещо магическо, което можем да направим в основния метод. Ние можем да определим нивото на достъп на нашия конструктор, което прави възможно създаването на обекти от нашия клас:
declaredConstructor.setAccessible(true);
Резултат от създаването на обект
Employee{name='Rob', surname='Stark', age=-1}
Ние не задаваме възрастта в нашия конструктор, така че той остава същият, Howто когато е бил инициализиран.
Прекрасно, нека обобщим!
Предимства от създаването на обекти с помощта на Constructor.newInstance()
И двата метода имат едно и също име, но има разлики между тях:
Class.newInstance() | Constructor.newInstance() |
---|---|
Може да извика само конструктор без аргументи . | Може да извика всеки конструктор, независимо от броя на параметрите. |
Изисква конструкторът да бъде видим. | Може да се обади и на частни конструктори при определени обстоятелства. |
Изхвърля всяко изключение (проверено or не), което е декларирано от конструктора. | Винаги обвива хвърлено изключение с InvocationTargetException . |
Поради тези причини Constructor.newInstance() се предпочита пред Class.newInstance() и е методът, използван от различни рамки и API като Spring, Guava, Zookeeper, Jackson, Servlet и др.