![translation](https://cdn.durumis.com/common/trans.png)
To jest post przetłumaczony przez AI.
Wybierz język
Tekst podsumowany przez sztuczną inteligencję durumis
- Refleksja to API, które umożliwia dostęp do informacji o klasach w czasie wykonywania, umożliwiając manipulowanie klasami w dowolny sposób.
- Dzięki refleksji można tworzyć instancje klas i uzyskiwać dostęp do pól i metod, niezależnie od modyfikatorów dostępu, co jest przydatne zwłaszcza w dużych projektach, gdzie zarządzanie zależnościami odbywa się dynamicznie.
- Należy jednak pamiętać, że może to naruszać hermetyzację i prowadzić do spadku wydajności, dlatego należy go używać tylko wtedy, gdy jest to konieczne.
Co to jest Reflection?
Reflection to API, które pozwala na tworzenie instancji danej klasy za pomocą obiektu typu Class załadowanego do obszaru sterty i umożliwia dostęp do pól i metod instancji niezależnie od modyfikatorów dostępu.
W tym kontekście załadowana klasa oznacza, że ładowacz klas JVM zakończył ładowanie pliku klasowego i utworzył obiekt typu Classzawierający informacje o tej klasie, który jest przechowywany w obszarze sterty. Należy pamiętać, że różni się to od obiektów tworzonych za pomocą słowa kluczowego new. Jeśli masz problemy z zrozumieniem obiektów typu Class, zapoznaj się z dokumentacją JDK dotyczącą obiektu java.lang.class.
Jak używać Reflection?
Aby móc korzystać z Reflection, najpierw musisz pobrać obiekt typu Class załadowany do obszaru sterty. Istnieją trzy sposoby na to.
- Pobieranie za pomocą Class.class
- Pobieranie za pomocą instance.getClass()
- Pobieranie za pomocą Class.forName("nazwa_klasy")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("Hasło to 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Jayon", 23, "Rozwój Darathu");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{nazwa_pakietu}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Wynik działania
1740000325
1740000325
Możesz zauważyć, że wszystkie trzy metody zwracają ten sam obiekt typu Class. Niezależnie od użytej metody, wartość skrótu jest taka sama, dlatego możesz wybrać odpowiednią metodę w zależności od sytuacji.
Teraz, korzystając z pobranego obiektu typu Class, możesz tworzyć instancje tej klasy i uzyskiwać dostęp do pól i metod instancji niezależnie od modyfikatorów dostępu. Zacznijmy od utworzenia instancji tej klasy.
public class Main {
public static void main(String[] args) throws Exception {
// Wyświetlenie wszystkich konstruktorów klasy Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Utworzenie instancji za pomocą konstruktora domyślnego klasy Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Utworzenie instancji za pomocą innego konstruktora klasy Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayon", 23, "Rozwój Darathu");
System.out.println("member3 = " + member3);
}
}
// Wynik działania
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
Możesz użyć metody getConstructor() do pobrania konstruktora i metody newInstance() do dynamicznego utworzenia instancji klasy Member.
Na koniec przyjrzyjmy się, jak uzyskać dostęp do pól i metod instancji niezależnie od modyfikatorów dostępu.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayon", 23, "Rozwój Darathu");
Class extends Member> memberClass = member.getClass();
// Dostęp do pól
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jayon2");
System.out.println(member);
// Dostęp do metod
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Testowanie Reflection");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Wynik działania
Jayon
23
Rozwój Darathu
Member{name='Jayon2', age=23, hobby='Rozwój Darathu'}
Testowanie Reflection
Możesz użyć metody getDeclaredFileds() do pobrania wszystkich zmiennych instancji klasy i metod get() i set() do odczytu i modyfikacji wartości pól. Pamiętaj, że aby uzyskać dostęp do pól z modyfikatorem dostępu private, musisz ustawić wartość true dla parametru metody setAccessible().
Podobnie, możesz użyć metody getDeclaredMethod() do pobrania metod. W tym przypadku musisz przekazać nazwę metody i typy parametrów jako argumenty. Podobnie jak w przypadku pól, aby uzyskać dostęp do metod z modyfikatorem dostępu private, musisz ustawić wartość true dla parametru metody setAccessible(). Na koniec możesz użyć metody invoke() do wywołania pobranej za pomocą API Reflection metody.
Zalety i wady
- Zalety
- Reflection zapewnia elastyczność, umożliwiając tworzenie instancji klas i uzyskiwanie dostępu do pól i metod w czasie wykonywania, niezależnie od modyfikatorów dostępu.
- Wady
- Reflection narusza zasadę hermetyzacji.
- Ponieważ instancje są tworzone w czasie wykonywania, typ nie może być sprawdzony w czasie kompilacji.
- Tworzenie instancji w czasie wykonywania utrudnia śledzenie przepływu programu.
- Dostęp do pól i metod za pomocą Reflection jest zazwyczaj wolniejszy niż bezpośredni dostęp. (Nie dotyczy to wszystkich sytuacji).
Dlaczego używać Reflection?
API Reflection umożliwia dostęp do informacji o klasach i manipulację nimi w czasie wykonywania. Możliwe jest nawet modyfikowanie pól i metod z modyfikatorem dostępu private. Może się to wydawać naruszeniem zasady hermetyzacji i techniką, której należy unikać.
W niewielkich aplikacjach konsolowych programista może łatwo określić wszystkie obiekty i zależności używane w programie w czasie kompilacji. Jednak w przypadku rozbudowanych frameworków, takich jak Spring, śledzenie wszystkich obiektów i zależności może być trudne. W takich przypadkach Reflection pozwala na dynamiczne tworzenie klas i budowanie zależności.
Na przykład w fabryce Beanów w Spring, wystarczy dodać adnotacje @Controller, @Service, @Repository, aby fabryka Beanów automatycznie tworzyła i zarządzała klasami z tymi adnotacjami. Programista nie musi informować fabryki Beanów o tych klasach, a to możliwe jest dzięki Reflection. W czasie wykonywania, fabryka Beanów skanuje i znajduje klasy z tymi adnotacjami, a następnie tworzy ich instancje za pomocą Reflection, wstrzykuje niezbędne pola i zapisuje je w fabryce Beanów.
Należy jednak pamiętać, że jak wspomniano wcześniej, Reflection narusza zasadę hermetyzacji. Należy go używać tylko w razie potrzeby.