Hent felt fra en Java-klasse ved hjelp av refleksjon

1. Oversikt

Refleksjon er muligheten for dataprogramvare til å inspisere strukturen under kjøretid. I Java oppnår vi dette ved å bruke Java Reflection API. Det lar oss inspisere elementene i en klasse, for eksempel felt, metoder eller til og med indre klasser, alt sammen ved kjøretid.

Denne opplæringen vil fokusere på hvordan du kan hente ut feltene i en Java-klasse, inkludert private og nedarvede felt.

2. Henter felt fra en klasse

La oss først se på hvordan du kan hente ut feltene i en klasse, uavhengig av deres synlighet. Senere vil vi se hvordan du også får arvede felt.

La oss starte med et eksempel på en Person klasse med to String Enger: etternavn og fornavn. Førstnevnte er beskyttet (det vil være nyttig senere) mens sistnevnte er det privat:

offentlig klasse Person {beskyttet streng etternavn; privat streng fornavn; }

Vi ønsker å få begge deler etternavn og fornavn felt ved hjelp av refleksjon. Vi oppnår dette ved å bruke Klasse :: getDeclaredFields metode. Som navnet antyder, returnerer dette alt erklært felt i en klasse, i form av en Felt matrise:

offentlig klasse PersonAndEmployeeReflectionUnitTest {/ * ... konstanter ... * / @Test offentlig ugyldighet givenPersonClass_whenGetDeclaredFields_thenTwoFields () {Field [] allFields = Person.class.getDeclaredFields (); assertEquals (2, allFields.length); assertTrue (Arrays.stream (allFields) .anyMatch (field -> field.getName (). equals (LAST_NAME_FIELD) && field.getType (). equals (String.class))); assertTrue (Arrays.stream (allFields) .anyMatch (field -> field.getName (). equals (FIRST_NAME_FIELD) && field.getType (). equals (String.class))); }}

Som vi kan se, får vi de to feltene i Person klasse. Vi sjekker navnene og typene som samsvarer med feltdefinisjonene i Person klasse.

3. Hente ut arvede felt

La oss nå se hvordan du får de arvede feltene i en Java-klasse.

For å illustrere dette, la oss lage en andre klasse med navnet Ansatt utvide Person, med et eget felt:

offentlig klasse Ansatt utvider Person {public int employeeId; }

3.1. Henter ned arvede felt på et enkelt klassehierarki

Ved hjelp av Employee.class.getDeclaredFields () ville bare returnere Ansatt ID felt, siden denne metoden ikke returnerer feltene deklarert i superklasser. For å også få arvelige felt må vi også få feltene til Person superklasse.

Selvfølgelig kunne vi bruke getDeclaredFields () metode på begge Person og Ansatt klasser og slå resultatene sammen i en enkelt matrise. Men hva om vi ikke eksplisitt ønsker å spesifisere superklassen?

I dette tilfellet, vi kan bruke en annen metode for Java Reflection API: Klasse :: getSuperclass. Dette gir oss superklassen til en annen klasse, uten at vi trenger å vite hva den superklassen er.

La oss samle resultatene av getDeclaredFields ()Ansatt.klasse og Employee.class.getSuperclass () og slå dem sammen i en enkelt gruppe:

@Test offentlig ugyldighet gittEmployeeClass_whenGetDeclaredFieldsOnBothClasses_thenThreeFields () {Field [] personFields = Employee.class.getSuperclass (). GetDeclaredFields (); Felt [] employeeFields = Employee.class.getDeclaredFields (); Felt [] allFields = nytt felt [medarbeiderFelts.lengde + personFelts.lengde]; Arrays.setAll (allFields, i -> (i <personFields.length? PersonFields [i]: ansatteFields [i - personFields.length])); assertEquals (3, allFields.length); Felt lastNameField = allFields [0]; assertEquals (LAST_NAME_FIELD, lastNameField.getName ()); assertEquals (String.class, lastNameField.getType ()); Felt firstNameField = allFields [1]; assertEquals (FIRST_NAME_FIELD, firstNameField.getName ()); assertEquals (String.class, firstNameField.getType ()); FeltmedarbeiderIdField = allFields [2]; assertEquals (EMPLOYEE_ID_FIELD, ansatteIdField.getName ()); assertEquals (int.class, employeeIdField.getType ()); }

Vi kan se her at vi har samlet de to feltene i Person samt enkeltfeltet for Ansatt.

Men, er det privat innen Person virkelig et arvelig felt? Ikke så mye. Det ville være det samme for en pakke-privat felt. Kun offentlig og beskyttet felt regnes som arvet.

3.2. Filtrering offentlig og beskyttet Enger

Dessverre tillater ingen metoder i Java API oss å samles offentlig og beskyttet felt fra en klasse og dens superklasser. De Klasse :: getFields metoden nærmer seg vårt mål når den returnerer alt offentlig felt i en klasse og dens superklasser, men ikke beskyttet de.

Den eneste måten vi har å få bare nedarvede felt er å bruke getDeclaredFields () som vi nettopp gjorde, og filtrere resultatene ved hjelp av Felt :: getModifiers metode. Denne returnerer en int som representerer modifikatorene for det gjeldende feltet. Hver mulige modifikator tildeles en effekt på to mellom 2^0 og 2^7.

For eksempel, offentlig er 2^0 og statisk er 2^3. Derfor kaller getModifiers () metode på en offentlig og statisk feltet ville returnere 9.

Da er det mulig å utføre en bitvis og mellom denne verdien og verdien til en bestemt modifikator for å se om det feltet har den modifikatoren. Hvis operasjonen returnerer noe annet enn 0, blir modifikatoren brukt, ellers ikke.

Vi er heldige da Java gir oss en verktøyklasse for å sjekke om modifikatorer er til stede i verdien som returneres av getModifiers (). La oss bruke isPublic () og isProtected () metoder for å samle bare arvede felt i vårt eksempel:

Liste 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 (felt -> field.getName (). tilsvarer (LAST_NAME_FIELD) && field.getType (). tilsvarer (String.class)));

Som vi kan se, bærer ikke resultatet det privat felt lenger.

3.3. Henter ned arvede felt på et dypt klassehierarki

I eksemplet ovenfor jobbet vi med et enkelt klassehierarki. Hva gjør vi nå hvis vi har et dypere klassehierarki og ønsker å samle alle de arvede feltene?

La oss anta at vi har en underklasse på Ansatt eller en superklasse av Person - for å oppnå feltene i hele hierarkiet vil det være nødvendig å sjekke alle superklassene.

Vi kan oppnå det ved å lage en verktøymetode som går gjennom hierarkiet, og bygge det komplette resultatet for oss:

Liste getAllFields (Class clazz) {if (clazz == null) {return Collections.emptyList (); } Listeresultat = ny ArrayList (getAllFields (clazz.getSuperclass ())); Liste filteredFields = Arrays.stream (clazz.getDeclaredFields ()) .filter (f -> Modifier.isPublic (f.getModifiers ()) || Modifier.isProtected (f.getModifiers ())) .collect (Collectors.toList () ); result.addAll (filteredFields); returresultat; }

Denne rekursive metoden vil søke offentlig og beskyttet felt gjennom klassehierarkiet og returnerer alt som er funnet i a Liste.

La oss illustrere det med en liten test på en ny Måned Ansatt klasse, utvide Ansatt en:

offentlig klasse MånedMedarbeider utvider Ansatt {beskyttet dobbel belønning; }

Denne klassen definerer et nytt felt - belønning. Gitt alle hierarkiklasser, bør metoden vår gi oss følgende felt definisjoner: Person :: etternavn, ansatt :: ansatteId og Måned Ansatt :: belønning.

La oss kalle getAllFields () metode på Måned Ansatt:

@Test offentlig ugyldig givenMonthEmployeeClass_whenGetAllFields_thenThreeFields () {List allFields = getAllFields (MonthEmployee.class); assertEquals (3, allFields.size ()); assertTrue (allFields.stream (). anyMatch (felt -> field.getName (). tilsvarer (LAST_NAME_FIELD) && field.getType (). tilsvarer (String.class))); assertTrue (allFields.stream (). anyMatch (felt -> field.getName (). er lik (EMPLOYEE_ID_FIELD) && field.getType (). er lik (int.klasse))); assertTrue (allFields.stream (). anyMatch (felt -> field.getName (). tilsvarer (MONTH_EMPLOYEE_REWARD_FIELD) && field.getType (). tilsvarer (double.class))); }

Som forventet samler vi alle offentlig og beskyttet Enger.

4. Konklusjon

I denne artikkelen så vi hvordan du kan hente inn feltene i en Java-klasse ved hjelp av Java Reflection API.

Vi lærte først hvordan du kan hente de deklarerte feltene i en klasse. Etter det så vi hvordan vi også kunne hente superklassefeltene. Så lærte vi å filtrere ut ikke-offentlig og ikke-beskyttet Enger.

Til slutt så vi hvordan vi kunne bruke alt dette for å samle de arvede feltene i et flerklassehierarki.

Som vanlig er den fulle koden for denne artikkelen tilgjengelig på vår GitHub.


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