Przykład tworzenia obiektu za pomocą Class.newInstance()
Wyobraź sobie: stoisz przed zadaniem: musisz stworzyć obiekt za pomocą odbicia. Zaczynamy?
Zacznijmy od początkowej klasy, którą chcemy uzyskać:
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 +
'}';
}
}
To będzie nasza klasa, w której jest kilka pól, konstruktor z parametrami, gettery i settery, metoda toString() i blok inicjalizacyjny. Przejdźmy teraz do drugiej części - tworzenia obiektu za pomocą odbicia. Pierwszym sposobem, któremu się przyjrzymy, będzie implementacja za pomocą 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());
}
}
Świetnie, uruchommy nasz kod i poczekajmy na dane wyjściowe wieku z inicjatora. Ale otrzymujemy błąd o braku domyślnego konstruktora. Okazuje się, że tą metodą możemy otrzymać jedynie obiekt utworzony za pomocą domyślnego konstruktora. Dodajmy domyślny konstruktor dla naszej klasy i ponownie przetestujmy kod.
Komunikat o błędzie:
Nowy kod konstruktora:
public Employee() { }
Wynik działania programu po dodaniu konstruktora:
Świetnie, odkryliśmy, jak działa ta metoda. Teraz zajrzyjmy pod maskę. Otwieramy dokumentację i widzimy, że nasza metoda jest już przestarzała ( Angielski - przestarzały ):
Może również rzucić InstantiationException , IllegalAccessException . Dlatego dokumentacja sugeruje, abyśmy skorzystali z drugiego sposobu tworzenia obiektu, a mianowicie Constructor.newInstance() . Przeanalizujmy działanie klasy Constructor i porozmawiajmy o tym bardziej szczegółowo.
metody getConstructors i getDeclaredConstructors
Aby pracować z klasą Constructor , najpierw musimy ją zdobyć. Mamy do tego dwie metody: getConstructors i getDeclaredConstructors .
Pierwsza zwraca listę publicznych konstruktorów jako tablicę, podczas gdy druga zwraca listę wszystkich konstruktorów klas jako tablicę.
Dodajmy trochę prywatności do naszej klasy, a mianowicie stworzymy kilka prywatnych konstruktorów, aby wyświetlić działanie naszych metod.
Dodaj kilku prywatnych konstruktorów:
private Employee(String name, String surname) {
this.name = name;
this.lastName = lastName;
}
W strukturze naszej klasy widać wyraźnie, że jeden z konstruktorów jest prywatny:
Testowanie naszych metod:
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);
}
}
}
I otrzymujemy taki wynik:
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 en.codegym.Employee(java.lang.String,java.lang.String,int)
public en.codegym.Employee()
Świetnie, więc mamy dostęp do klasy Constructor . Teraz możemy mówić o jego możliwościach.
Klasa java.lang.reflect.Constructor, jej główne metody
Przyjrzyjmy się głównym metodom i ich działaniu:
metoda | Opis |
---|---|
pobierzNazwę() | Zwraca nazwę tego konstruktora jako ciąg znaków. |
getModifiers() | Zwraca modyfikatory języka Java jako liczbę. |
getExceptionType() | Zwraca tablicę obiektów klasy reprezentujących typy wyjątków zadeklarowanych przez konstruktora. |
getParameters() | Zwraca tablicę obiektów Parameter reprezentujących wszystkie parametry. Zwraca tablicę o długości 0, jeśli konstruktor nie ma parametrów. |
getParameterTypes() | Zwraca tablicę obiektów klas reprezentujących formalne typy parametrów w kolejności deklaracji. |
getGenericParameterTypes() | Zwraca tablicę obiektów typu reprezentujących formalne typy parametrów w kolejności deklaracji. |
getName() & getModifiers()
Zapakujmy naszą tablicę w Listę, aby ułatwić nam pracę, i napiszmy metody getName i 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;
}
I nasza główna metoda , w której będziemy nazywać wszystko:
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);
}
W rezultacie zobaczymy wszystkie potrzebne nam informacje:
Konstruktory:
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Modyfikatory :
[prywatny, publiczny, publiczny]
getExceptionType()
Ta metoda pozwala nam uzyskać tablicę wyjątków, które może zgłosić nasz konstruktor. Zmodyfikujmy jeden z naszych konstruktorów i napiszmy nową metodę.
Tutaj nieznacznie zmodyfikowaliśmy naszego obecnego prywatnego konstruktora:
private Employee(String name, String surname) throws Exception {
this.name = name;
this.lastName = lastName;
}
A tutaj mamy metodę uzyskiwania typów wyjątków i dodawania ich do 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);
Powyżej zwróciliśmy się do pierwszego konstruktora z naszej listy. Jak zdobyć konkretnego konstruktora, omówimy nieco później.
I spójrz na dane wyjściowe po dodaniu throws Exception :
[klasa java.lang.Exception]
A przed dodaniem wyjątku:
[]
Wszystko fajnie tylko jak sprawdzić jakie parametry są potrzebne naszym konstruktorom? Przyjrzyjmy się tej części.
getParameters() & getParameterTypes() & getGenericParameterTypes()
Zacznijmy jeszcze raz od dopracowania naszego prywatnego konstruktora. Teraz będzie to wyglądać tak:
private Employee(String name, String surname, List<String> list) {
this.name = name;
this.lastName = lastName;
}
Mamy też trzy dodatkowe metody: getParameters do pobrania kolejności parametrów i ich typów, getParameterTypes do pobrania typów parametrów oraz getGenericParameterTypes do pobrania typów opakowanych w generyczne .
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()));
}
A w naszej już nie tak małej głównej dodajemy jeszcze kilka informacji:
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);
Jeśli spojrzymy na nasz wynik, otrzymamy bardzo szczegółowe dane o parametrach naszych konstruktorów:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Typy parametrów:
[class java.lang.String, class java.lang.String, interfejs java.util.List]
Typy parametrów konstruktora:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]
Tutaj możesz wyraźnie zobaczyć różnicę między każdą metodą. Widzimy, że można osobno uzyskać dane o typach parametrów, dane o klasach opakowań i ogólnie o wszystkim. Super! Zakończyliśmy naszą znajomość z klasą Constructor i teraz możemy wrócić do głównego tematu naszego artykułu - tworzenia obiektów.
Tworzenie obiektu za pomocą Constructor.newInstance()
Drugim sposobem tworzenia obiektów jest wywołanie metody newInstance na konstruktorze. Rzućmy okiem na działający przykład i zobaczmy, w jaki sposób otrzymujemy konkretnego konstruktora.
Jeśli Twoim zadaniem jest uzyskanie jednego konstruktora, musisz użyć metody getConstructor (nie mylić z metodą getConstructors , która zwraca tablicę wszystkich konstruktorów). Metoda getConstructor zwraca domyślny konstruktor.
public static void main(String[] args) throws NoSuchMethodException {
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor();
System.out.println(employeeConstructor);
}
A jeśli chcemy uzyskać konkretny konstruktor, musimy przekazać tej metodzie typy parametrów, które będą w konstruktorze.
Nie zapominajmy, że możemy uzyskać naszego prywatnego konstruktora tylko za pomocą metody getDeclaredConstructor .
Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);
W ten sposób możemy otrzymać konkretnego konstruktora. Spróbujmy teraz utworzyć obiekt z prywatnego i publicznego konstruktora.
Konstruktor publiczny:
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);
W rezultacie mamy obiekt, z którym możemy dalej pracować:
Pracownik{imię='NeIvan' nazwisko='NeIvanov', wiek=10}
Wszystko działa świetnie! Teraz próbujemy z prywatnym konstruktorem:
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());
W rezultacie otrzymamy błąd dotyczący prywatności naszego konstruktora:
Java nie mogła utworzyć obiektu za pomocą tego konstruktora, ale w metodzie main można zrobić trochę magii . Ustawi dostępność dla naszego konstruktora i pozwoli na tworzenie obiektów naszej klasy:
declaredConstructor.setAccessible(true);
Wynik utworzenia obiektu:
Pracownik{imię='NeIvan', nazwisko='NeIvanov', wiek=-1}
Nie ustawiamy wieku w naszym konstruktorze i pozostaje on taki sam, jak podczas inicjalizacji.
Super, czas na podsumowanie!
Korzyści z tworzenia za pomocą Constructor.newInstance()
Obie metody wyglądają podobnie w nazwie, ale istnieją między nimi różnice:
Class.newInstance() | Konstruktor. nowaInstancja() |
---|---|
Może wywoływać tylko konstruktora no-arg . | Może wywołać dowolnego konstruktora niezależnie od liczby parametrów. |
Wymaga, aby konstruktor był widoczny. | W pewnych okolicznościach może również wywoływać prywatnych konstruktorów. |
Zgłasza każdy wyjątek (zaznaczony lub nie) zadeklarowany przez konstruktora. | Zawsze otacza zgłoszony wyjątek InvocationTargetException . |
Z tych powodów metoda Constructor.newInstance() jest preferowana w porównaniu z Class.newInstance() i jest używana przez różne frameworki i interfejsy API, takie jak Spring, Guava, Zookeeper, Jackson, Servlet itp.
GO TO FULL VERSION