אחזר שדות ממחלקת Java באמצעות השתקפות

1. סקירה כללית

השתקפות היא היכולת של תוכנת מחשב לבדוק את המבנה שלה בזמן הריצה. ב- Java אנו משיגים זאת באמצעות ה- ממשק ה- API של Java Reflection. זה מאפשר לנו לבדוק את האלמנטים של הכיתה כגון שדות, שיטות או אפילו שיעורים פנימיים, הכל בזמן ריצה.

הדרכה זו תתמקד כיצד לאחזר את השדות של מחלקת Java, כולל שדות פרטיים וירושה.

2. אחזור שדות משיעור

בואו נסתכל תחילה כיצד לאחזר את שדות הכיתה, ללא קשר לנראות שלהם. בהמשך נראה כיצד להשיג גם שדות בירושה.

נתחיל בדוגמה של א אדם כיתה עם שניים חוּט שדות: שם משפחה ו שם פרטי. הראשון הוא מוּגָן (זה יהיה שימושי מאוחר יותר) בעוד שזה האחרון פְּרָטִי:

אדם בכיתה ציבורית {מוגן שם משפחה של מחרוזת; פרטי מחרוזת firstName; }

אנחנו רוצים להשיג את שניהם שם משפחה ו שם פרטי שדות באמצעות השתקפות. נשיג זאת באמצעות ה- מחלקה :: getDeclaredFields שיטה. כשמו כן הוא, זה מחזיר את כל מוּצהָר שדות של כיתה, בצורה של שדה מַעֲרָך:

מחלקה ציבורית PersonAndEmployeeReflectionUnitTest {/ * ... קבועים ... * / @Test public void givenPersonClass_whenGetDeclaredFields_thenTwoFields () {Field [] allFields = Person.class.getDeclaredFields (); assertEquals (2, allFields.length); assertTrue (Arrays.stream (allFields) .anyMatch (field -> field.getName (). שווה (LAST_NAME_FIELD) && field.getType (). שווה (String.class))); assertTrue (Arrays.stream (allFields) .anyMatch (שדה -> field.getName (). שווה (FIRST_NAME_FIELD) && field.getType (). שווה (String.class))); }}

כפי שאנו רואים, אנו מקבלים את שני השדות של ה- אדם מעמד. אנו בודקים את שמותיהם וסוגיהם התואמים להגדרות השדות ב- אדם מעמד.

3. אחזור שדות בירושה

בואו נראה כעת כיצד להשיג את השדות שעברו בירושה של מחלקת Java.

כדי להמחיש זאת, בואו ליצור מחלקה שנייה בשם עוֹבֵד מַאֲרִיך אדם, עם שדה משלו:

מעמד ציבורי עובד מרחיב את האדם {public int employeeId; }

3.1. אחזור שדות בירושה בהיררכיית כיתות פשוטה

באמצעות Employee.class.getDeclaredFields () רק יחזיר את תג עובד שדה, מכיוון ששיטה זו אינה מחזירה את השדות המוצהרים בכיתות-על. כדי לקבל גם שדות בירושה עלינו לקבל גם את השדות של אדם מעמד-על.

כמובן, נוכל להשתמש ב- getDeclaredFields () שיטה על שניהם אדם ו עוֹבֵד שיעורים ומיזוג תוצאותיהם למערך יחיד. אבל מה אם אנחנו לא רוצים לציין במפורש את מעמד העל?

במקרה הזה, אנו יכולים להשתמש בשיטה אחרת של ממשק ה- API של Java Reflection: מחלקה :: getSuperclass. זה נותן לנו את מעמד העל של מעמד אחר, מבלי שנצטרך לדעת מה זה אותו מעמד-על.

בואו לאסוף את התוצאות של getDeclaredFields () עַל שכיר עובד ו Employee.class.getSuperclass () ולמזג אותם למערך יחיד:

@Test הציבור בטל givenEmployeeClass_whenGetDeclaredFieldsOnBothClasses_thenThreeFields () {Field [] personFields = Employee.class.getSuperclass (). GetDeclaredFields (); שדה [] employeeFields = Employee.class.getDeclaredFields (); שדה [] allFields = שדה חדש [עובדFields.length + personFields.length]; Arrays.setAll (allFields, i -> (i <personFields.length? PersonFields [i]: employeeFields [i - personFields.length])); assertEquals (3, allFields.length); שדה lastNameField = allFields [0]; assertEquals (LAST_NAME_FIELD, lastNameField.getName ()); assertEquals (String.class, lastNameField.getType ()); שדה firstNameField = allFields [1]; assertEquals (FIRST_NAME_FIELD, firstNameField.getName ()); assertEquals (String.class, firstNameField.getType ()); שדה עובדIdField = allFields [2]; assertEquals (EMPLOYEE_ID_FIELD, employeeIdField.getName ()); assertEquals (int.class, employeeIdField.getType ()); }

אנו יכולים לראות כאן שאספנו את שני התחומים של אדם כמו גם השדה היחיד של עוֹבֵד.

אבל, האם פְּרָטִי שדה של אדם באמת שדה תורשתי? לא כל כך. זה יהיה אותו הדבר עבור א חבילה-פרטית שדה. רק פּוּמְבֵּי ו מוּגָן שדות נחשבים לירושה.

3.2. סִנוּן פּוּמְבֵּי ו מוּגָן שדות

למרבה הצער, שום שיטה ב- API של Java אינה מאפשרת לנו להתכנס פּוּמְבֵּי ו מוּגָן שדות ממעמד וכיתות העל שלו. ה מחלקה :: getFields השיטה מתקרבת למטרה שלנו כשהיא מחזירה הכל פּוּמְבֵּי שדות כיתה ומעמדות העל שלה, אבל לא מוּגָן יחידות.

הדרך היחידה שיש לנו להשיג רק שדות בירושה היא להשתמש ב- getDeclaredFields () כפי שעשינו זה עתה, ולסנן את תוצאותיה באמצעות שדה :: getModifiers שיטה. זה מחזיר int המייצג את השינויים של השדה הנוכחי. לכל שינוי אפשרי מוקצה כוח של שניים בין 2^0 ו 2^7.

לדוגמה, פּוּמְבֵּי הוא 2^0 ו סטָטִי הוא 2^3. לכן קורא ל getModifiers () שיטה על א פּוּמְבֵּי ו סטָטִי שדה יחזיר 9.

לאחר מכן, ניתן לבצע א סיבית ו בין ערך זה לערך של שינוי ספציפי כדי לראות אם באותו שדה יש ​​את אותו שינוי. אם הפעולה מחזירה משהו אחר מ- 0, השינוי מוחל, אחרת לא.

יש לנו מזל כיוון שג'אווה מספקת לנו מחלקת כלי עזר כדי לבדוק אם ישנם שינויים בערך שהוחזר על ידי getModifiers (). בואו נשתמש ב- isPublic () ו isProtected () שיטות לאיסוף רק שדות בירושה בדוגמה שלנו:

רשימת 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 (שדה -> field.getName (). שווה (LAST_NAME_FIELD) && field.getType (). שווה (String.class)));

כפי שאנו רואים, התוצאה אינה נושאת את פְּרָטִי שדה יותר.

3.3. אחזור שדות בירושה בהיררכיה מעמדית עמוקה

בדוגמה שלעיל עבדנו על היררכיה מעמדית אחת. מה עושים עכשיו אם יש לנו היררכיה מעמדית עמוקה יותר ונרצה לאסוף את כל השדות שעברו בירושה?

נניח שיש לנו תת-מחלקה של עוֹבֵד או מעמד-על של אדם - ואז השגת שדות ההיררכיה כולה תידרש לבדוק את כל מחלקות העל.

אנו יכולים להשיג זאת על ידי יצירת שיטת שירות שעוברת דרך ההיררכיה ובניית התוצאה המלאה עבורנו:

רשום getAllFields (clazz class) {if (clazz == null) {return Collections.emptyList (); } תוצאת רשימה = ArrayList חדש (getAllFields (clazz.getSuperclass ())); רשימה filteredFields = Arrays.stream (clazz.getDeclaredFields ()) .filter (f -> Modifier.isPublic (f.getModifiers ()) || Modifier.isProtected (f.getModifiers ())) .collect (Collectors.toList () ); result.addAll (filteredFields); תוצאת החזרה; }

שיטה רקורסיבית זו תחפש פּוּמְבֵּי ו מוּגָן שדות דרך היררכיית הכיתה ומחזירה את כל מה שנמצא ב- רשימה.

בואו נמחיש זאת במבחן קטן על חדש חודש עובד בכיתה, הרחבת ה - עוֹבֵד אחד:

מעמד ציבורי MonthEmployee מאריך את העובד {תגמול כפול מוגן; }

מחלקה זו מגדירה תחום חדש - פרס. בהתחשב בכל מחלקת ההיררכיה, השיטה שלנו צריכה לתת לנו את השדות הבאים הגדרות: אדם :: שם משפחה, עובד :: עובד מזהה ו חודש עובד :: תגמול.

בוא נקרא getAllFields () שיטה ב חודש עובד:

@Test הציבור בטל givenMonthEmployeeClass_whenGetAllFields_thenThreeFields () {רשימה allFields = getAllFields (MonthEmployee.class); assertEquals (3, allFields.size ()); assertTrue (allFields.stream (). anyMatch (שדה -> field.getName (). שווה (LAST_NAME_FIELD) && field.getType (). שווה (String.class))); assertTrue (allFields.stream (). anyMatch (שדה -> field.getName (). שווה (EMPLOYEE_ID_FIELD) && field.getType (). שווה (int.class))); assertTrue (allFields.stream (). anyMatch (field -> field.getName (). שווה (MONTH_EMPLOYEE_REWARD_FIELD) && field.getType (). שווה (double.class))); }

כצפוי, אנו אוספים את כל ה פּוּמְבֵּי ו מוּגָן שדות.

4. מסקנה

במאמר זה ראינו כיצד לאחזר את השדות של מחלקת Java באמצעות ה- ממשק ה- API של Java Reflection.

למדנו לראשונה כיצד לאחזר את השדות המוצהרים של כיתה. לאחר מכן ראינו כיצד לאחזר גם את שדות הסופר-קלאס שלה. ואז למדנו לסנן שאינם-פּוּמְבֵּי ולא-מוּגָן שדות.

לבסוף ראינו כיצד ליישם את כל אלה כדי לאסוף את השדות שעברו בירושה של היררכיה מעמדית מרובה.

כרגיל, הקוד המלא של מאמר זה זמין באתר GitHub שלנו.


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