Dohvaćanje polja iz Java klase pomoću refleksije

1. Pregled

Refleksija je sposobnost računalnog softvera da provjeri svoju strukturu tijekom izvođenja. U Javi to postižemo korištenjem Java Reflection API. Omogućuje nam pregledavanje elemenata klase kao što su polja, metode ili čak unutarnje klase, sve za vrijeme izvođenja.

Ovaj će se vodič usredotočiti na to kako dohvatiti polja Java klase, uključujući privatna i naslijeđena polja.

2. Dohvaćanje polja iz razreda

Pogledajmo prvo kako dohvatiti polja klase, bez obzira na njihovu vidljivost. Kasnije ćemo vidjeti kako doći i do naslijeđenih polja.

Počnimo s primjerom a Osoba razred s dvoje Niz polja: prezime i ime. Prvi je zaštićen (to će biti korisno kasnije) dok ovo drugo jest privatni:

javna klasa Person {zaštićeni niz lastName; private String firstName; }

Želimo dobiti oboje prezime i ime polja pomoću refleksije. To ćemo postići pomoću Klasa :: getDeclaredFields metoda. Kao što mu samo ime govori, ovo vraća sve proglasio polja klase, u obliku a Polje niz:

javna klasa PersonAndEfficieeReflectionUnitTest {/ * ... konstante ... * / @Test javna praznina givenPersonClass_whenGetDeclaredFields_thenTwoFields () {Field [] allFields = Person.class.getDeclaredFields (); assertEquals (2, allFields.length); assertTrue (Arrays.stream (allFields) .anyMatch (polje -> field.getName (). je jednako (LAST_NAME_FIELD) && field.getType (). jednako (String.class))); assertTrue (Arrays.stream (allFields) .anyMatch (field -> field.getName (). je jednako (FIRST_NAME_FIELD) && field.getType (). jednako (String.class))); }}

Kao što vidimo, dobivamo dva polja Osoba razred. Provjeravamo njihova imena i tipove koji odgovaraju definicijama polja u Osoba razred.

3. Dohvaćanje naslijeđenih polja

Pogledajmo sada kako doći do naslijeđenih polja Java klase.

Da to ilustriramo, stvorimo drugi razred s imenom Zaposlenik produžujući Osoba, s vlastitim poljem:

public class Employee proširuje Person {public int workerId; }

3.1. Dohvaćanje naslijeđenih polja na hijerarhiji jednostavne klase

Koristeći Employee.class.getDeclaredFields () bi samo vratio id zaposlenika polje, jer ova metoda ne vraća polja deklarirana u superklasama. Da bismo također dobili naslijeđena polja, moramo dobiti i polja Osoba superrazred.

Naravno, mogli bismo koristiti getDeclaredFields () metoda na oba Osoba i Zaposlenik klase i spoji njihove rezultate u jedan niz. Ali što ako ne želimo izričito navesti superklasu?

U ovom slučaju, možemo se poslužiti drugom metodom Java Reflection API: Klasa :: getSuperclass. To nam daje superklasu druge klase, a da ne moramo znati koja je to superklasa.

Prikupimo rezultate getDeclaredFields () na Zaposlenik.razred i Employee.class.getSuperclass () i spojite ih u jedan niz:

@Test javna praznina givenEfficieeClass_whenGetDeclaredFieldsOnBothClasses_thenThreeFields () {Field [] personFields = Employee.class.getSuperclass (). GetDeclaredFields (); Polje [] workerFields = Employee.class.getDeclaredFields (); Polje [] allFields = novo polje [staffFields.length + personFields.length]; Arrays.setAll (allFields, i -> (i <personFields.length? PersonFields [i]: workerFields [i - personFields.length])); assertEquals (3, allFields.length); Polje lastNameField = allFields [0]; assertEquals (LAST_NAME_FIELD, lastNameField.getName ()); assertEquals (String.class, lastNameField.getType ()); Polje firstNameField = allFields [1]; assertEquals (FIRST_NAME_FIELD, firstNameField.getName ()); assertEquals (String.class, firstNameField.getType ()); Polje workerIdField = allFields [2]; assertEquals (EMPLOYEE_ID_FIELD, workerIdField.getName ()); assertEquals (int.class, workerIdField.getType ()); }

Ovdje možemo vidjeti da smo okupili dva polja Osoba kao i jedinstveno polje Zaposlenik.

Ali, jest privatni polje od Osoba stvarno naslijeđeno polje? Ne tako puno. To bi bilo isto za a paket-privatni polje. Samo javnost i zaštićen polja se smatraju naslijeđenim.

3.2. Filtriranje javnost i zaštićen Polja

Nažalost, nijedna metoda u Java API-ju ne dopušta prikupljanje javnost i zaštićen polja iz klase i njenih superklasa. The Klasa :: getFields metoda približava se našem cilju jer vraća sve javnost polja klase i njezine superklase, ali ne i zaštićen one.

Jedini način na koji moramo dobiti samo naslijeđena polja je korištenje getDeclaredFields () metodu, kao što smo upravo učinili, i filtrirajte rezultate pomoću Polje :: getModifiers metoda. Ovaj vraća an int koji predstavljaju modifikatore trenutnog polja. Svakom mogućem modifikatoru dodijeljena je snaga dva između 2^0 i 2^7.

Na primjer, javnost je 2^0 i statički je 2^3. Stoga pozivanje getModifiers () metoda na a javnost i statički polje bi se vratilo 9.

Tada je moguće izvesti a bitovno i između ove vrijednosti i vrijednosti određenog modifikatora kako bi se vidjelo ima li to polje taj modifikator. Ako operacija vraća nešto drugo od 0, tada se primjenjuje modifikator, inače ne.

Sretni smo jer nam Java nudi klasu uslužnih programa za provjeru jesu li modifikatori prisutni u vrijednosti koju je vratio getModifiers (). Iskoristimo isPublic () i isProtected () metode za prikupljanje samo naslijeđenih polja u našem primjeru:

Popis personFields = Arrays.stream (Employee.class.getSuperclass (). GetDeclaredFields ()) .filter (f -> Modifier.isPublic (f.getModifiers ()) || Modifier.isProtected (f.getModifiers ())) .collect (Collectors.toList ()); assertEquals (1, personFields.size ()); assertTrue (personFields.stream (). anyMatch (polje -> field.getName (). je jednako (LAST_NAME_FIELD) && field.getType (). jednako (String.class)));

Kao što vidimo, rezultat ne nosi rezultat privatni polje više.

3.3. Dohvaćanje naslijeđenih polja na dubokoj hijerarhiji klase

U gornjem primjeru radili smo na hijerarhiji jedne klase. Što ćemo sada ako imamo dublju hijerarhiju klasa i želimo prikupiti sva naslijeđena polja?

Pretpostavimo da imamo podrazred od Zaposlenik ili superklasa od Osoba - tada će za dobivanje polja cijele hijerarhije trebati provjeriti sve superklase.

To možemo postići stvaranjem korisne metode koja prolazi kroz hijerarhiju, gradeći za nas cjelovit rezultat:

Popis getAllFields (klasa klazz) {if (clazz == null) {return Collections.emptyList (); } Rezultat popisa = novi ArrayList (getAllFields (clazz.getSuperclass ())); Popis filteredFields = Arrays.stream (clazz.getDeclaredFields ()) .filter (f -> Modifier.isPublic (f.getModifiers ()) || Modifier.isProtected (f.getModifiers ())) .collect (Collectors.toList () ); result.addAll (filteredFields); povratni rezultat; }

Ova rekurzivna metoda će pretraživati javnost i zaštićen polja kroz hijerarhiju razreda i vraća sve što je pronađeno u a Popis.

Ilustrirajmo to malim testom na novom MjesecZaposlenik razreda, proširujući Zaposlenik jedan:

javni razred MonthE Employee produžuje Employee {zaštićena dvostruka nagrada; }

Ova klasa definira novo polje - nagrada. S obzirom na svu hijerarhijsku klasu, naša bi nam metoda trebala dati sljedeća polja definicije: Osoba :: Prezime, zaposlenik :: id zaposlenika i Mjesec zaposlenika :: nagrada.

Nazovimo getAllFields () metoda na MjesecZaposlenik:

@Test javna praznina givenMonthEfficieeClass_whenGetAllFields_thenThreeFields () {Popis svihFields = getAllFields (MonthEfficiee.class); assertEquals (3, allFields.size ()); assertTrue (allFields.stream (). anyMatch (polje -> field.getName (). je jednako (LAST_NAME_FIELD) && field.getType (). jednako (String.class))); assertTrue (allFields.stream (). anyMatch (polje -> field.getName (). je jednako (EMPLOYEE_ID_FIELD) && field.getType (). jednako (int.class))); assertTrue (allFields.stream (). anyMatch (polje -> field.getName (). je jednako (MONTH_EMPLOYEE_REWARD_FIELD) && field.getType (). jednako (dvostruka klasa))); }

Očekivano, okupljamo sve javnost i zaštićen polja.

4. Zaključak

U ovom smo članku vidjeli kako dohvatiti polja Java klase pomoću Java Reflection API.

Prvo smo naučili kako dohvatiti deklarirana polja klase. Nakon toga smo vidjeli kako dohvatiti i njegova polja superklase. Zatim smo naučili filtrirati ne-javnost i ne-zaštićen polja.

Napokon, vidjeli smo kako sve to primijeniti za prikupljanje naslijeđenih polja višestruke hijerarhije klasa.

Kao i obično, puni kod za ovaj članak dostupan je na našem GitHubu.


$config[zx-auto] not found$config[zx-overlay] not found