انعكاس جافا يوفر القدرة على فحص وتعديل سلوك التطبيق أثناء التشغيل. الانعكاس في جافا هو أحد مواضيع الجافا الأساسية المتقدمة. باستخدام انعكاس جافا ، يمكننا فحص هيكل الفئة ، الواجهة ، التعداد ، والحصول على معلومات حول طرقها وحقولها أثناء التشغيل على الرغم من أن الفئة ليست متاحة أثناء وقت الترجمة. يمكننا أيضًا استخدام الانعكاس لإنشاء كائن ، استدعاء طرقه ، وتغيير قيم الحقول.
انعكاس جافا
- الانعكاس في جافا
- انعكاس جافا للصفوف
- الحصول على كائن الصف
- الحصول على الصف الأم
- الحصول على الفئات الأعضاء العامة
- الحصول على الفئات المعلن عنها
- الحصول على الفئة الطالبة
- الحصول على اسم الحزمة
- الحصول على تعديلات الفئة
- الحصول على معلمات النوع
- الحصول على الواجهات المطبقة
- الحصول على جميع الطرق العامة
- الحصول على جميع البنايات العامة
- الحصول على جميع الحقول العامة
- الحصول على جميع التعليقات
- تحليل الجافا للحقول
- تحليل الجافا للأساليب
- تحليل الجافا للبناة
- الانعكاس في جافا للتعليقات
الانعكاس في جافا مفهوم قوي جدًا ويكاد لا يكون له استخدام في البرمجة العادية ولكنه هو العمود الفقري لمعظم الأطر الجافا وجافا 2EE. بعض الأطر التي تستخدم انعكاس جافا هي:
- JUnit – يستخدم الانعكاس لتحليل التعليقات @Test للحصول على طرق الاختبار ثم استدعائها.
- Spring – حقن التبعية، اقرأ المزيد في حقن التبعية في Spring
- Tomcat حاوية الويب لتوجيه الطلب إلى الوحدة الصحيحة عن طريق تحليل ملفات web.xml وطلب URI الخاص بهم.
- إكليبس الإكمال التلقائي لأسماء الطرق
- Struts
- Hibernate
القائمة لا تنتهي وجميعها تستخدم تعكس جافا لأن جميع هذه الأطر ليس لديها معرفة ووصول إلى الفئات المحددة من قبل المستخدم والواجهات وطرقها إلخ. لا ينبغي أن نستخدم التعكس في البرمجة العادية حيث لدينا بالفعل وصول إلى الفئات والواجهات بسبب العيوب التالية.
- أداء ضعيف – نظرًا لأن تعكس جافا يحل الأنواع بشكل ديناميكي، فإنه يتضمن معالجة مثل مسح مسار الفئة للعثور على الفئة لتحميلها، مما يسبب أداءً بطيئًا.
- قيود الأمان – يتطلب التعكس أذونات التشغيل التي قد لا تكون متاحة للنظام الذي يعمل تحت مدير الأمان. يمكن أن يتسبب هذا في فشل تطبيقك أثناء التشغيل بسبب مدير الأمان.
- قضايا الأمان – باستخدام التعكس، يمكننا الوصول إلى جزء من الشيفرة التي ليس من المفترض أن نصل إليها، على سبيل المثال يمكننا الوصول إلى الحقول الخاصة في فئة وتغيير قيمتها. يمكن أن يكون هذا تهديدًا للأمان خطيرًا ويؤدي إلى سلوك غير طبيعي لتطبيقك.
- صيانة عالية – تعتبر شفرة التعكس صعبة الفهم والتصحيح، وأي مشاكل في الشفرة لا يمكن العثور عليها في وقت الترجمة لأن الفئات قد لا تكون متاحة، مما يجعلها أقل مرونة وصعبة الصيانة.
في جافا، كل كائن إما نوع أساسي أو مرجع. جميع الصفوف، والتعدادات، والمصفوفات هي أنواع مرجعية وترث من java.lang.Object
. الأنواع الأساسية هي – boolean، byte، short، int، long، char، float، و double. java.lang.Class هو نقطة الدخول لجميع عمليات الانعكاس. لكل نوع من الكائنات، ينشئ JVM مثيلًا لا يتغير من java.lang.Class
يوفر الطرق لفحص خصائص التشغيل للكائن وإنشاء كائنات جديدة، واستدعاء طرقه، والحصول على/تعيين حقول الكائن. في هذا القسم، سننظر في الطرق المهمة للصف Class، للراحة، أنشئ بعض الصفوف والواجهات ذات تسلسل وراثي.
package com.journaldev.reflection;
public interface BaseInterface {
public int interfaceInt=0;
void method1();
int method2(String str);
}
package com.journaldev.reflection;
public class BaseClass {
public int baseInt;
private static void method3(){
System.out.println("Method3");
}
public int method4(){
System.out.println("Method4");
return 0;
}
public static int method5(){
System.out.println("Method5");
return 0;
}
void method6(){
System.out.println("Method6");
}
// الصف الداخلي العام
public class BaseClassInnerClass{}
//عضو الفصل العام
public enum BaseClassMemberEnum{}
}
package com.journaldev.reflection;
@Deprecated
public class ConcreteClass extends BaseClass implements BaseInterface {
public int publicInt;
private String privateString="private string";
protected boolean protectedBoolean;
Object defaultObject;
public ConcreteClass(int i){
this.publicInt=i;
}
@Override
public void method1() {
System.out.println("Method1 impl.");
}
@Override
public int method2(String str) {
System.out.println("Method2 impl.");
return 0;
}
@Override
public int method4(){
System.out.println("Method4 overriden.");
return 0;
}
public int method5(int i){
System.out.println("Method4 overriden.");
return 0;
}
// فصول داخلية
public class ConcreteClassPublicClass{}
private class ConcreteClassPrivateClass{}
protected class ConcreteClassProtectedClass{}
class ConcreteClassDefaultClass{}
//عضو enum
enum ConcreteClassDefaultEnum{}
public enum ConcreteClassPublicEnum{}
//واجهة العضو
public interface ConcreteClassPublicInterface{}
}
دعونا نلقي نظرة على بعض أهم أساليب الانعكاس للفصول.
احصل على كائن الفصل
يمكننا الحصول على فئة كائن باستخدام ثلاث طرق – من خلال المتغير الثابت class
، باستخدام طريقة getClass()
للكائن و java.lang.Class.forName(String fullyClassifiedClassName)
. بالنسبة لأنواع البيانات الأساسية والمصفوفات، يمكننا استخدام المتغير الثابت class
. توفر الفئات المغلفة متغيرًا ثابتًا آخر TYPE
للحصول على الفئة.
// الحصول على فئة باستخدام الانعكاس
Class > concreteClass = ConcreteClass.class;
concreteClass = new ConcreteClass(5).getClass();
try {
// تستخدم الطريقة أدناه في معظم الأحيان في الإطارات مثل JUnit
// حقن تبعية الربيع، حاوية الويب تومكات
// إكليبس الاكمال التلقائي لأسماء الطرق، هايبرنيت، ستراتس2 الخ
// لأن ConcreteClass غير متوفرة في وقت الترجمة
concreteClass = Class.forName("com.journaldev.reflection.ConcreteClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(concreteClass.getCanonicalName()); // prints com.journaldev.reflection.ConcreteClass
// بالنسبة لأنواع البيانات الأساسية وفئات الغلاف والمصفوفات
Class > booleanClass = boolean.class;
System.out.println(booleanClass.getCanonicalName()); // prints boolean
Class > cDouble = Double.TYPE;
System.out.println(cDouble.getCanonicalName()); // prints double
Class > cDoubleArray = Class.forName("[D");
System.out.println(cDoubleArray.getCanonicalName()); //prints double[]
Class > twoDStringArray = String[][].class;
System.out.println(twoDStringArray.getCanonicalName()); // prints java.lang.String[][]
getCanonicalName()
يعيد الاسم القنوي للفئة الأساسية. لاحظ أن java.lang.Class يستخدم التعميمات، وهو يساعد الأطر في التأكد من أن الفئة المُسترجعة هي فئة فرعية من فئة الأساس في الأطر. تحقق من دليل تعلم تعميمات جافا لتتعرف على التعميمات والرموز البارزة لها.
الحصول على الفئة الأم
طريقة getSuperclass() على كائن فئة تعيد الفئة الأم للفئة. إذا كانت هذه الفئة تمثل إما فئة Object، أو واجهة، أو نوعاً أساسياً، أو فارغ، فسيتم إرجاع قيمة null. إذا كان هذا الكائن يمثل فئة مصفوفة، فسيتم إرجاع كائن الفئة الذي يمثل فئة Object.
Class<?> superClass = Class.forName("com.journaldev.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass); // prints "class com.journaldev.reflection.BaseClass"
System.out.println(Object.class.getSuperclass()); // prints "null"
System.out.println(String[][].class.getSuperclass());// prints "class java.lang.Object"
الحصول على فئات الأعضاء العمومية
طريقة `getClasses()` من التمثيل الفئوي للكائن ترجع مصفوفة تحتوي على كائنات فئة تمثل جميع الفئات العامة، الواجهات والتعدادات التي تعتبر أعضاء في الفئة الممثلة بواسطة هذا الكائن الفئة. يشمل ذلك الفئات العامة وأعضاء الواجهة الموروثة من الفئات الأساسية وأعضاء الفئة العامة والواجهة التي تم إعلانها بواسطة الفئة. تُرجع هذه الطريقة مصفوفة طولها 0 إذا كان لهذا الكائن الفئة ليس لديه فئات أو واجهات فرعية عامة أو إذا كان يمثل هذا الكائن الفئة نوعًا أساسيًا، أو فئة مصفوفة، أو فارغة.
Class >[] classes = concreteClass.getClasses();
//[class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum,
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface,
//class com.journaldev.reflection.BaseClass$BaseClassInnerClass,
//class com.journaldev.reflection.BaseClass$BaseClassMemberEnum]
System.out.println(Arrays.toString(classes));
الحصول على الفئات المعلنة
طريقة `getDeclaredClasses()` ترجع مصفوفة من كائنات الفئة تعكس جميع الفئات والواجهات المعلنة كأعضاء في الفئة الممثلة بواسطة هذا الكائن الفئة. المصفوفة المُرجعة لا تتضمن الفئات المعلنة في الفئات الموروثة والواجهات.
//الحصول على جميع الفئات، واجهات البرمجة التطبيقية، والتعدادات التي يتم تعريفها صراحة في ConcreteClass
Class >[] explicitClasses = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredClasses();
//يطبع [class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass،
//class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultEnum،
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPrivateClass،
//class com.journaldev.reflection.ConcreteClass$ConcreteClassProtectedClass،
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass،
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum،
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface]
System.out.println(Arrays.toString(explicitClasses));
الحصول على الفئة المعلنة
getDeclaringClass()
تُرجع الأسلوب الكائن الفئة الممثلة للفئة التي تم تعريفها فيها.
Class > innerClass = Class.forName("com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass");
//يطبع com.journaldev.reflection.ConcreteClass
System.out.println(innerClass.getDeclaringClass().getCanonicalName());
System.out.println(innerClass.getEnclosingClass().getCanonicalName());
الحصول على اسم الحزمة
getPackage()
تُرجع الأسلوب الحزمة لهذه الفئة. يُستخدم جار التحميل لهذه الفئة للعثور على الحزمة. يمكننا استدعاء أسلوب getName()
من Package للحصول على اسم الحزمة.
//prints "com.journaldev.reflection"
System.out.println(Class.forName("com.journaldev.reflection.BaseInterface").getPackage().getName());
الحصول على المعرّفات للفصيلة
getModifiers()
تُرجع الدالة تمثيل int لمعرّفات الفصيلة، يمكننا استخدام الدالة java.lang.reflect.Modifier.toString()
للحصول عليها بتنسيق سلسلة كما هو مستخدم في الشيفرة المصدرية.
System.out.println(Modifier.toString(concreteClass.getModifiers())); //prints "public"
//prints "public abstract interface"
System.out.println(Modifier.toString(Class.forName("com.journaldev.reflection.BaseInterface").getModifiers()));
الحصول على معلمات النوع
getTypeParameters()
تُرجع مصفوفة من النوع TypeVariable إذا كانت هناك أي معلمات نوع مرتبطة بالفصيلة. يتم إرجاع معلمات النوع بنفس الترتيب الذي تم الإعلان عنه.
//الحصول على معلمات النوع (الجنريكية)
TypeVariable >[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters();
for(TypeVariable > t : typeParameters)
System.out.print(t.getName()+",");
الحصول على الواجهات المنفذة
getGenericInterfaces()
تُرجع الدالة مصفوفة الواجهات التي تم تنفيذها بالفصيلة مع معلومات النوع الجنريكي. يمكننا أيضًا استخدام getInterfaces()
للحصول على تمثيل الفصيلة لجميع الواجهات المنفذة.
Type[] interfaces = Class.forName("java.util.HashMap").getGenericInterfaces();
"[\u0646\u0648\u0639 \u0627\u0644\u062e\u0631\u064a\u0637\u0629 \u0627\u0644\u0645\u0635\u0646\u0648\u0639ة, \u0642\u064a\u0645\u0629 \u0646\u0648\u0639 \u062c\u0627\u0641\u0627., \u0633\u0644\u0633\u0644\u0629 \u062c\u0627\u0641\u0627.]"
System.out.println(Arrays.toString(interfaces));
"[\u0633\u0644\u0633\u0644\u0629 \u062c\u0627\u0641\u0627., \u0642\u064a\u0645\u0629 \u0646\u0648\u0639 \u062c\u0627\u0641\u0627., \u0633\u0644\u0633\u0644\u0629 \u062c\u0627\u0641\u0627.]"
System.out.println(Arrays.toString(Class.forName("java.util.HashMap").getInterfaces()));
\u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u0637\u0631\u0642 \u0627\u0644\u0639\u0627\u0645\u0629
\u0627\u0644\u062d\u0635\u0648\u0644\u200f\u200f \u0639\u0644\u0649 \u0627\u0644\u0637\u0631\u0642 \u0028\u0029
\u0627\u0644\u0637\u0631\u064a\u0642\u0629 \u0028\u0029 \u062a\u0639\u064a\u062f \u0627\u0644\u0645\u0635\u0631\u0627\u062d \u0627\u0644\u0645\u0635\u0646\u0639\u0629 \u0645\u0646 \u0627\u0644\u0643\u0644\u0627\u0633 \u0628\u0645\u0627 \u064a\u0634\u0645\u0644 \u0627\u0644\u0637\u0631\u0642 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0644\u0643\u0644\u0627\u0633 \u0627\u0644\u062e\u0627\u0635\u0629 \u0648\u0627\u0644\u0648\u0627\u0636\u062d\u0629 \u0627\u0644\u0645\u0646\u0627\u0641\u0630 \u0648\u0648\u0627\u062c\u0647 \u0627\u0644\u0645\u063a\u0646\u0637\u0648\u0646\u0629\u002e
Method[] publicMethods = Class.forName("com.journaldev.reflection.ConcreteClass").getMethods();
\u0627\u0644\u0637\u0631\u0642 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0643\u0644\u0627\u0633 \u0645\u062a\u0643\u0648\u0646 \u0645\u0646 \u0643\u0644\u0627\u0633 \u062e\u0627\u0635 \u0628\u0627\u0644\u0639\u0645\u0644\u064a\u0629\u060c \u0643\u0644\u0627\u0633 \u0627\u0644\u0641\u0631\u0639 \u0627\u0644\u0645\u0648\u0627\u0644\u064a \u0648\u0643\u0644\u0627\u0633 \u0627\u0644\u0627\u0646\u062a\u0631\u0646\u062a\u060c \u0648\u0627\u0644\u0637\u0631\u0642 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0644\u0643\u0644\u0627\u0633 \u0627\u0644\u062e\u0627\u0635\u0629 \u0648\u0627\u0644\u0648\u0627\u0636\u062d\u0629 \u0627\u0644\u0645\u0646\u0627\u0641\u0630 \u0648\u0648\u0627\u062c\u0647 \u0627\u0644\u0645\u063a\u0646\u0637\u0648\u0646\u0629\u002e
System.out.println(Arrays.toString(publicMethods));
\u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0639\u0627\u0645\u0629
\u0627\u0644\u062d\u0635\u0648\u0644\u200f\u200f \u0639\u0644\u0649 \u0627\u0644\u0628\u0646\u0627\u0621 \u0028\u0029
\u0627\u0644\u0637\u0631\u064a\u0642\u0629 \u0028\u0029 \u062a\u0639\u064a\u062f \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0644\u0645\u0631\u062c\u0639 \u0627\u0644\u0643\u0644\u0627\u0633\u064a \u0644\u0643\u0627\u0626\u0646\u002e
\u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0639\u0627\u0645\u0629
\u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0639\u0627\u0645\u0629
Constructor >[] publicConstructors = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructors();
\u0627\u0644\u0628\u0646\u0627\u0621 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0643\u0644\u0627\u0633 \u0645\u062a\u0643\u0648\u0646 \u0645\u0646 \u0643\u0644\u0627\u0633 \u062e\u0627\u0635 \u0628\u0627\u0644\u0639\u0645\u0644\u064a\u0629\u002e
System.out.println(Arrays.toString(publicConstructors));
\u0627\u0644\u062d\u0635\u0648\u0644 \u0639\u0644\u0649 \u062c\u0645\u064a\u0639 \u0627\u0644\u062d\u0642\u0648\u0644 \u0627\u0644\u0639\u0627\u0645\u0629
\u0627\u0644\u062d\u0635\u0648\u0644\u200f\u200f \u0639\u0644\u0649 \u0627\u0644\u062d\u0642\u0648\u0644 \u0028\u0029
\u0627\u0644\u0637\u0631\u064a\u0642\u0629 \u0028\u0029 \u062a\u0639\u064a\u062f \u0627\u0644\u0645\u0635\u0631\u0627\u062d \u0627\u0644\u0645\u0635\u0646\u0639\u0629 \u0645\u0646 \u0627\u0644\u062d\u0642\u0648\u0644 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0644\u0643\u0644\u0627\u0633 \u0628\u0645\u0627 \u064a\u0634\u0645\u0644 \u0627\u0644\u062d\u0642\u0648\u0644 \u0627\u0644\u0639\u0627\u0645\u0629 \u0644\u0644\u0643\u0644\u0627\u0633 \u0627\u0644\u062e\u0627\u0635\u0629 \u0648\u0627\u0644\u0648\u0627\u0636\u062d\u0629 \u0627\u0644\u0645\u0646\u0627\u0641\u0630 \u0648\u0648\u0627\u062c\u0647 \u0627\u0644\u0645\u063a\u0646\u0637\u0648\u0646\u0629\u002e
//الحصول على جميع الحقول العامة
Field[] publicFields = Class.forName("com.journaldev.reflection.ConcreteClass").getFields();
//تطبع الحقول العامة للـ ConcreteClass، والفئة الأساسية لها والواجهات الأساسية الخاصة بها
System.out.println(Arrays.toString(publicFields));
الحصول على جميع التعليقات
getAnnotations()
تُرجع الطريقة كل التعليقات للعنصر، يمكننا استخدامها مع الفئة والحقول والطرق أيضًا. يُرجى ملاحظة أن التعليقات المتاحة فقط باستخدام reflection تكون لها سياسة استبقاء RUNTIME، تحقق من دليل تعليقات جافا. سننظر في هذا بالتفصيل في الأقسام لاحقًا.
java.lang.annotation.Annotation[] annotations = Class.forName("com.journaldev.reflection.ConcreteClass").getAnnotations();
//تطبع [@java.lang.Deprecated()]
System.out.println(Arrays.toString(annotations));
يوفر واجهة Reflection العديد من الطرق لتحليل حقول الفئة وتعديل قيمها أثناء التشغيل، في هذا القسم سننظر في بعض الوظائف الشائعة المستخدمة لـ reflection للطرق.
الحصول على الحقل العام
في القسم السابق، رأينا كيفية الحصول على قائمة جميع الحقول العامة لفئة. توفر واجهة برمجة التطبيقات Reflection أيضًا طريقة للحصول على حقل عام محدد من فئة معينة من خلال طريقة getField()
. تبحث هذه الطريقة عن الحقل في مرجع الفئة المحددة ثم في الواجهات الفرعية ومن ثم في الفئات الفرعية.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
سيُرجع الاستدعاء أعلاه الحقل من BaseInterface الذي يتم تنفيذه بواسطة ConcreteClass. إذا لم يتم العثور على حقل، فسيتم رمي استثناء NoSuchFieldException.
فئة تعريف الحقل
يمكننا استخدام getDeclaringClass()
من كائن الحقل للحصول على الفئة التي تعرف الحقل.
try {
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
Class<?> fieldClass = field.getDeclaringClass();
System.out.println(fieldClass.getCanonicalName()); //prints com.journaldev.reflection.BaseInterface
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
الحصول على نوع الحقل
تُرجع طريقة getType() كائن الفئة لنوع الحقل المعلن، إذا كان الحقل من نوع أساسي، فإنها ترجع كائن الفئة للصنف المحتوي.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
Class<?> fieldType = field.getType();
System.out.println(fieldType.getCanonicalName()); //prints int
الحصول على/تعيين قيمة الحقل العام
يمكننا الحصول على قيمة الحقل وتعيينها في كائن باستخدام الانعكاس.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
ConcreteClass obj = new ConcreteClass(5);
System.out.println(field.get(obj)); //prints 5
field.setInt(obj, 10); //setting field value to 10 in object
System.out.println(field.get(obj)); //prints 10
تُرجع الطريقة get() كائنًا، لذا إذا كان الحقل من النوع الأساسي، فإنها تُرجع الفئة Wrapper المقابلة. إذا كان الحقل ثابتًا، يمكننا تمرير الكائن كقيمة null في طريقة get(). هناك عدة طرق set*() لتعيين كائن إلى الحقل أو تعيين مختلف أنواع الأنواع الأساسية إلى الحقل. يمكننا الحصول على نوع الحقل ثم استدعاء الدالة الصحيحة لتعيين قيمة الحقل بشكل صحيح. إذا كان الحقل نهائيًا، فإن الطرق set() تُلقي java.lang.IllegalAccessException.
الحصول على/تعيين قيمة الحقل الخاص
نعلم أن الحقول والطرق الخاصة لا يمكن الوصول إليها خارج الفئة ولكن باستخدام الانعكاس يمكننا الحصول على/تعيين قيمة الحقل الخاص عن طريق إيقاف التحقق من الوصول في جافا لتعديلات الحقل.
Field privateField = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredField("privateString");
// إيقاف التحقق من الوصول مع استدعاء الطريقة أدناه
privateField.setAccessible(true);
ConcreteClass objTest = new ConcreteClass(1);
System.out.println(privateField.get(objTest)); // prints "private string"
privateField.set(objTest, "private string updated");
System.out.println(privateField.get(objTest)); //prints "private string updated"
من خلال الانعكاس، يمكننا الحصول على معلومات حول طريقة ويمكننا أيضًا استدعائها. في هذا القسم، سنتعلم طرقًا مختلفة للحصول على الطريقة، استدعاء الطريقة والوصول إلى الطرق الخاصة.
الحصول على الطريقة العامة
يمكننا استخدام `getMethod()` للحصول على طريقة عامة من الفئة، حيث نحتاج إلى تمرير اسم الطريقة وأنواع المعلمات الخاصة بها. إذا لم يتم العثور على الطريقة في الفئة، يقوم واجهة برمجة التطبيقات للانعكاس بالبحث عن الطريقة في الفئة الأم. في المثال أدناه، أحصل على طريقة `put()` من HashMap باستخدام الانعكاس. يظهر المثال أيضًا كيفية الحصول على أنواع المعلمات، والوسوم الخاصة بالطريقة، ونوع العائد للطريقة.
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
//الحصول على أنواع معلمات الطريقة، يقوم بالطباعة "[class java.lang.Object, class java.lang.Object]"
System.out.println(Arrays.toString(method.getParameterTypes()));
//الحصول على نوع العائد للطريقة، يعيد "class java.lang.Object"، مرجع الفئة لـ void
System.out.println(method.getReturnType());
//الحصول على وسوم الطريقة
System.out.println(Modifier.toString(method.getModifiers())); //prints "public"
استدعاء الطريقة العامة
يمكننا استخدام طريقة invoke() من كائن Method لاستدعاء الطريقة، في الكود المثال أدناه أقوم باستدعاء الطريقة put على HashMap باستخدام الانعكاس.
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class);
Map<String, String> hm = new HashMap<>();
method.invoke(hm, "key", "value");
System.out.println(hm); // prints {key=value}
إذا كانت الطريقة ثابتة، يمكننا تمرير قيمة NULL كوسيط الكائن.
استدعاء الطرق الخاصة
يمكننا استخدام getDeclaredMethod() للحصول على الطريقة الخاصة ثم إيقاف التحقق من الوصول لاستدعائها، يوضح المثال أدناه كيف يمكننا استدعاء الطريقة method3() من BaseClass التي هي ثابتة ولا تحتوي على معلمات.
//استدعاء الطريقة الخاصة
Method method = Class.forName("com.journaldev.reflection.BaseClass").getDeclaredMethod("method3", null);
method.setAccessible(true);
method.invoke(null, null); //prints "Method3"
توفر واجهة برمجة التكامل (Reflection API) أساليب للحصول على بناة الفئة لتحليلها، ويمكننا إنشاء مثيلات جديدة للفئة عن طريق استدعاء البناء. لقد تعلمنا بالفعل كيفية الحصول على جميع بناة الفئة العامة.
الحصول على بناء عام
يمكننا استخدام طريقة `getConstructor()` على تمثيل الفئة للكائن للحصول على بناء عام معين. يوضح المثال التالي كيفية الحصول على بناء الفئة ConcreteClass المعرفة أعلاه وبناء HashMap الذي لا يأخذ وسيطات. يظهر أيضًا كيفية الحصول على مصفوفة من أنواع المعلمات للبناء.
Constructor > constructor = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructor(int.class);
//الحصول على معلمات البناء
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"
Constructor > hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); // prints "[]"
إنشاء كائن باستخدام البناء
يمكننا استخدام طريقة `newInstance()` على كائن البناء لإنشاء مثيل جديد من الفئة. نظرًا لأننا نستخدم التكامل عندما لا نمتلك معلومات الفئات أثناء وقت الترجمة، يمكننا تعيينها إلى Object ثم استخدام التكامل بشكل إضافي للوصول إلى حقولها واستدعاء طرقها.
Constructor > constructor = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructor(int.class);
//الحصول على معلمات البناء
System.out.println(Arrays.toString(constructor.getParameterTypes())); // prints "[int]"
Object myObj = constructor.newInstance(10);
Method myObjMethod = myObj.getClass().getMethod("method1", null);
myObjMethod.invoke(myObj, null); //prints "Method1 impl."
Constructor > hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null);
System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); // prints "[]"
HashMap myMap = (HashMap) hashMapConstructor.newInstance(null);
تم إدخال التعليقات في جافا 1.5 لتوفير معلومات الفئة أو الطرق أو الحقول الوسيطة والآن يتم استخدامها بشكل كبير في الإطارات مثل الربيع والهايبرنيت. تم توسيع واجهة برمجة التطبيقات للانعكاس أيضًا لتوفير الدعم لتحليل التعليقات في وقت التشغيل. باستخدام واجهة برمجة التطبيقات للانعكاس يمكننا تحليل التعليقات التي سياسة الاحتفاظ بها هي وقت التشغيل. لقد كتبت بالفعل دورة تعليمية مفصلة حول التعليقات وكيفية استخدام واجهة برمجة التطبيقات للانعكاس لتحليل التعليقات، لذا أود أن أقترح عليك مراجعة دورة تعليمية حول تعليمات جافا. هذا كل شيء بالنسبة لمثال تعليمي حول انعكاس جافا، آمل أن تكون قد أعجبت بالتعليمي وفهمت أهمية واجهة برمجة التطبيقات للانعكاس في جافا.
Source:
https://www.digitalocean.com/community/tutorials/java-reflection-example-tutorial