Hent felter fra en Java-klasse ved hjælp af refleksion

1. Oversigt

Refleksion er computersoftwares evne til at inspicere dets struktur under kørsel. I Java opnår vi dette ved at bruge Java Reflection API. Det giver os mulighed for at inspicere elementerne i en klasse, såsom felter, metoder eller endda indre klasser, alt sammen under kørsel.

Denne tutorial vil fokusere på, hvordan man henter felterne i en Java-klasse, herunder private og nedarvede felter.

2. Hentning af felter fra en klasse

Lad os først se på, hvordan man henter felterne i en klasse, uanset deres synlighed. Senere vil vi se, hvordan man også får arvede felter.

Lad os starte med et eksempel på en Person klasse med to Snor felter: efternavn og fornavn. Den tidligere er beskyttet (det vil være nyttigt senere), mens sidstnævnte er privat:

offentlig klasse person {beskyttet streng efternavn; privat streng fornavn; }

Vi ønsker at få begge dele efternavn og fornavn felter ved hjælp af refleksion. Vi opnår dette ved at bruge Klasse :: getDeclaredFields metode. Som navnet antyder, returnerer dette alt erklæret felter i en klasse i form af en Mark matrix:

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

Som vi kan se, får vi de to felter i Person klasse. Vi kontrollerer deres navne og typer, der matcher feltdefinitionerne i Person klasse.

3. Hentning af arvelige felter

Lad os nu se, hvordan man får de arvede felter i en Java-klasse.

For at illustrere dette, lad os oprette en anden klasse med navnet Medarbejder udvider Person, med et eget felt:

offentlig klasse Medarbejder udvider Person {public int employeeId; }

3.1. Hentning af arvelige felter i et simpelt klassehierarki

Ved brug af Employee.class.getDeclaredFields () ville kun returnere Medarbejder-ID Mark, da denne metode ikke returnerer de felter, der er angivet i superklasser. For også at få arvede felter skal vi også få felterne i Person superklasse.

Selvfølgelig kunne vi bruge getDeclaredFields () metode på begge Person og Medarbejder klasser og flette deres resultater i et enkelt array. Men hvad hvis vi ikke udtrykkeligt ønsker at specificere superklassen?

I dette tilfælde, vi kan gøre brug af en anden metode til Java Reflection API: Klasse :: getSuperclass. Dette giver os superklassen i en anden klasse, uden at vi behøver at vide, hvad den superklasse er.

Lad os samle resultaterne af getDeclaredFields ()Medarbejderklasse og Employee.class.getSuperclass () og flet dem sammen i et enkelt array:

@Test offentlig ugyldighed givenEmployeeClass_whenGetDeclaredFieldsOnBothClasses_thenThreeFields () {Field [] personFields = Employee.class.getSuperclass (). GetDeclaredFields (); Felt [] medarbejderfelt = Medarbejder.klasse.getDeklareretFelt (); Felt [] allFields = nyt felt [medarbejderfelt.længde + personFelt.længde]; Arrays.setAll (allFields, i -> (i <personFields.length? PersonFields [i]: medarbejderFields [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 ()); MarkmedarbejderIdField = allFields [2]; assertEquals (EMPLOYEE_ID_FIELD, medarbejderIdField.getName ()); assertEquals (int.class, medarbejderIdField.getType ()); }

Vi kan se her, at vi har samlet de to felter i Person såvel som det enkelte felt af Medarbejder.

Men er det privat felt af Person virkelig et nedarvet felt? Ikke så meget. Det ville være det samme for en pakke-privat Mark. Kun offentlig og beskyttet felter betragtes som nedarvede.

3.2. Filtrering offentlig og beskyttet Felter

Desværre giver ingen metode i Java API os mulighed for at samle offentlig og beskyttet felter fra en klasse og dens superklasser. Det Klasse :: getFields metode nærmer sig vores mål, da den returnerer alle offentlig felter i en klasse og dens superklasser, men ikke beskyttet dem.

Den eneste måde, vi kun har arvede felter på, er at bruge getDeclaredFields () metode, som vi lige gjorde, og filtrere dens resultater ved hjælp af Felt :: getModifiers metode. Denne returnerer en int repræsenterer modifikatorerne for det aktuelle felt. Hver mulig modifikator tildeles en effekt på to imellem 2^0 og 2^7.

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

Derefter er det muligt at udføre en bitvis og mellem denne værdi og værdien af ​​en bestemt modifikator for at se, om dette felt har den modifikator. Hvis operationen returnerer noget andet end 0, anvendes modifikatoren, ellers ikke.

Vi er heldige, da Java giver os en hjælpeklasse til at kontrollere, om modifikatorer er til stede i den værdi, der returneres af getModifiers (). Lad os bruge isPublic () og isProtected () metoder til kun at samle nedarvede felter i vores 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 (). er lig med (LAST_NAME_FIELD) && field.getType (). er lig med (String.class)));

Som vi kan se, bærer resultatet ikke privat felt længere.

3.3. Henter arvede felter i et dybt klasseshierarki

I ovenstående eksempel arbejdede vi på et enkelt klassehierarki. Hvad gør vi nu, hvis vi har et dybere klassehierarki og ønsker at samle alle de nedarvede felter?

Lad os antage, at vi har en underklasse af Medarbejder eller en superklasse af Person - for at opnå felterne i hele hierarkiet skal du kontrollere alle superklasser.

Vi kan opnå det ved at oprette en hjælpemetode, der løber gennem hierarkiet og bygge det komplette resultat for os:

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 metode søger offentlig og beskyttet felter gennem klassehierarkiet og returnerer alt, hvad der er fundet i en Liste.

Lad os illustrere det med en lille test på en ny Måned Medarbejder klasse, udvide Medarbejder en:

offentlig klasse MånedMedarbejder udvider Medarbejder {beskyttet dobbelt belønning; }

Denne klasse definerer et nyt felt - belønning. I betragtning af hele hierarkiklassen skal vores metode give os følgende felter definitioner: Person :: efternavn, medarbejder :: medarbejder-id og Måned Medarbejder :: belønning.

Lad os kalde getAllFields () metode til Måned Medarbejder:

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

Som forventet samler vi alle de offentlig og beskyttet felter.

4. Konklusion

I denne artikel så vi, hvordan man henter felterne i en Java-klasse ved hjælp af Java Reflection API.

Vi lærte først, hvordan man henter de erklærede felter i en klasse. Derefter så vi, hvordan vi også kunne hente dets superklassefelter. Derefter lærte vi at filtrere ud ikke-offentlig og ikke-beskyttet felter.

Endelig så vi, hvordan vi anvendte alt dette til at samle de nedarvede felter i et multipel klassehierarki.

Som sædvanlig er den fulde kode til denne artikel tilgængelig på vores GitHub.


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