CodeGym /جاوا بلاگ /Random-SD /Reflection API: Reflection. جاوا جي اونداهي پاسي
John Squirrels
سطح
San Francisco

Reflection API: Reflection. جاوا جي اونداهي پاسي

گروپ ۾ شايع ٿيل
سلام، نوجوان پاداوان. هن آرٽيڪل ۾، مان توهان کي فورس بابت ٻڌايان ٿو، هڪ طاقت جيڪا جاوا پروگرامر صرف بظاهر ناممڪن حالتن ۾ استعمال ڪندا آهن. جاوا جي اونداهي طرف Reflection API آهي. جاوا ۾، عکاسي لاڳو ڪئي وئي آهي Java Reflection API استعمال ڪندي.

جاوا ريفريڪشن ڇا آهي؟

انٽرنيٽ تي هڪ مختصر، صحيح ۽ مشهور تعريف آهي. Reflection ( late لاطيني reflexio - to turn back ) ھڪ ميکانيزم آھي جيڪو ڪنھن پروگرام بابت ڊيٽا کي ڳولڻ لاءِ جڏھن اھو ھلندو ھجي. Reflection توهان کي معلومات ڳولڻ جي اجازت ڏئي ٿي فيلڊز، طريقن، ۽ ڪلاس جي تعمير ڪندڙن بابت. Reflection توهان کي انهن قسمن سان ڪم ڪرڻ جي اجازت ڏئي ٿي جيڪي گڏ ڪرڻ وقت موجود نه هئا، پر جيڪي رن ٽائيم دوران دستياب ٿي ويا. عڪس ۽ منطقي طور تي هڪجهڙائي وارو ماڊل غلط معلومات جاري ڪرڻ لاءِ اهو ممڪن بڻائي ٿو ته صحيح متحرڪ ڪوڊ ٺاهڻ. ٻين لفظن ۾، هڪ سمجھڻ ته ڪيئن عکاسي جاوا ۾ ڪم ڪري ٿي توهان جي لاءِ ڪيترائي شاندار موقعا کوليندا. توهان لفظي طور تي طبقن ۽ انهن جي اجزاء کي ڇڪي سگهو ٿا. هتي هڪ بنيادي فهرست آهي جنهن جي عڪاسي جي اجازت ڏئي ٿي:
  • سکو / هڪ اعتراض جي درجي جو تعين؛
  • ڪلاس جي موڊيفائرز، فيلڊز، طريقن، مستقل، تعمير ڪندڙ، ۽ سپر ڪلاسز بابت ڄاڻ حاصل ڪريو؛
  • معلوم ڪريو ته ڪهڙا طريقا لاڳو ٿيل انٽرفيس سان تعلق رکن ٿا؛
  • ھڪڙي ڪلاس جو ھڪڙو مثال ٺاھيو جنھن جي ڪلاس جو نالو اڻڄاتل آھي جيستائين هلائڻ وقت؛
  • حاصل ڪريو ۽ ھڪڙي شئي جي فيلڊ جا قدر مقرر ڪريو نالي سان؛
  • نالي سان هڪ اعتراض جي طريقي کي ڪال ڪريو.
Reflection لڳ ڀڳ سڀني جديد جاوا ٽيڪنالاجيز ۾ استعمال ڪيو ويندو آهي. اهو تصور ڪرڻ ڏکيو آهي ته جاوا، هڪ پليٽ فارم جي طور تي، بغير ڪنهن عڪاسي جي اهڙي وسيع اپنائڻ حاصل ڪري سگهي ٿي. گهڻو ڪري، اهو نه هوندو. هاڻي ته توهان عام طور تي هڪ نظرياتي تصور جي طور تي عڪاسي سان واقف آهيو، اچو ته ان جي عملي درخواست ڏانهن اڳتي وڌون! اسان Reflection API جا سڀ طريقا نه سکنداسون- صرف اھي طريقا جيڪي توھان اصل ۾ ملندا. جيئن ته عڪاسي ۾ ڪلاسن سان ڪم ڪرڻ شامل آهي، اسان هڪ سادي ڪلاس سان شروع ڪنداسين جنهن کي سڏيو ويندو آهي MyClass:
public class MyClass {
   private int number;
   private String name = "default";
//    public MyClass(int number, String name) {
//        this.number = number;
//        this.name = name;
//    }
   public int getNumber() {
       return number;
   }
   public void setNumber(int number) {
       this.number = number;
   }
   public void setName(String name) {
       this.name = name;
   }
   private void printData(){
       System.out.println(number + name);
   }
}
جئين توهان ڏسي سگهو ٿا، هي هڪ تمام بنيادي ڪلاس آهي. پيرا ميٽرز سان تعمير ڪندڙ عمدي طور تي تبصرو ڪيو ويو آهي. اسان ان کي بعد ۾ واپس آڻينداسين. جيڪڏهن توهان ڪلاس جي مواد کي غور سان ڏٺو، توهان شايد محسوس ڪيو ته نالي جي فيلڊ لاءِ حاصل ڪندڙ جي غير موجودگي . نالو فيلڊ پاڻ کي پرائيويٽ رسائي موڊيفائر سان نشان لڳايو ويو آهي : اسان ان کي ڪلاس کان ٻاهر تائين رسائي نٿا ڪري سگهون جنهن جو مطلب آهي ته اسان ان جي قيمت ٻيهر حاصل نٿا ڪري سگهون . پوءِ مسئلو ڇا آهي ؟ توهان بڌايو. " جيٽر شامل ڪريو يا رسائي موڊيفائر کي تبديل ڪريو". ۽ توھان صحيح ھوندؤ، جيستائين ھڪڙي مرتب ٿيل AAR لائبريري ۾ يا ڪنھن ٻئي پرائيويٽ ماڊل ۾ تبديل ڪرڻ جي صلاحيت نه ھجي. عملي طور تي، اهو هر وقت ٿئي ٿو. ۽ ڪجهه بي پرواهه پروگرامر صرف هڪ گيٽر لکڻ وساري ويٺا . اهو ئي وقت آهي عڪس کي ياد ڪرڻ جو! اچو ته ڪوشش ڪريون ته ڪلاس جي خانگي نالي واري فيلڊ تي وڃو : MyClassMyClass
public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; // No getter =(
   System.out.println(number + name); // Output: 0null
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(number + name); // Output: 0default
}
اچو ته تجزيو ڪريون ته ڇا ٿيو. جاوا ۾، اتي هڪ شاندار ڪلاس سڏيو ويندو آهي Class. اهو هڪ قابل عمل جاوا ايپليڪيشن ۾ ڪلاس ۽ انٽرفيس جي نمائندگي ڪري ٿو. اسان وچ ۾ لاڳاپا نه ڍڪينداسين Class۽ ClassLoader، ڇو ته اهو هن مضمون جو موضوع ناهي. اڳيون، هن طبقي جي شعبن کي ٻيهر حاصل ڪرڻ لاء، توهان کي getFields()طريقو سڏڻ جي ضرورت آهي. اهو طريقو هن طبقي جي سڀني پهچ وارن شعبن کي واپس ڏيندو. اهو اسان لاءِ ڪم نٿو ڪري، ڇاڪاڻ ته اسان جو ميدان خانگي آهي ، تنهن ڪري اسان اهو getDeclaredFields()طريقو استعمال ڪريون ٿا. اهو طريقو ڪلاس فيلڊز جي هڪ صف کي پڻ ڏئي ٿو، پر هاڻي ان ۾ نجي ۽ محفوظ شعبن شامل آهن. انهي صورت ۾، اسان ڄاڻون ٿا فيلڊ جو نالو جنهن ۾ اسان دلچسپي رکون ٿا، تنهنڪري اسان اهو getDeclaredField(String)طريقو استعمال ڪري سگهون ٿا، جتي Stringگهربل فيلڊ جو نالو آهي. نوٽ: getFields()۽ getDeclaredFields()والدين طبقي جا شعبا واپس نه ڪريو! زبردست. اسان کي اسان جي نالي جي حوالي سان هڪ Fieldاعتراض مليو . جيئن ته فيلڊ عوامي نه هئي ، اسان کي ان سان ڪم ڪرڻ جي رسائي فراهم ڪرڻ گهرجي. طريقو اسان کي اڳتي وڌڻ جي اجازت ڏئي ٿو. هاڻي نالو فيلڊ اسان جي مڪمل ڪنٽرول هيٺ آهي! توھان ان جي قيمت حاصل ڪري سگھو ٿا اعتراض جي طريقي کي ڪال ڪندي ، جتي اسان جي طبقي جو ھڪڙو مثال آھي . اسان قسم کي تبديل ڪريون ٿا ۽ قيمت کي اسان جي نالي جي متغير کي تفويض ڪريو. جيڪڏهن اسان نالي جي فيلڊ ۾ نئين قيمت مقرر ڪرڻ لاء هڪ سيٽر نه ڳولي سگهون ٿا ، توهان سيٽ جو طريقو استعمال ڪري سگهو ٿا: setAccessible(true)Fieldget(Object)ObjectMyClassString
field.set(myClass, (String) "new value");
مبارڪون هجن! توهان صرف ڌيان جي بنياديات ۾ مهارت حاصل ڪئي آهي ۽ هڪ خانگي فيلڊ تائين رسائي حاصل ڪئي آهي! بلاڪ تي ڌيان ڏيو try/catch، ۽ استثنا جي قسمن کي سنڀاليو پيو وڃي. IDE توهان کي ٻڌائيندو ته انهن جي موجودگي پنهنجي طور تي گهربل آهي، پر توهان واضح طور تي انهن جي نالن سان ٻڌائي سگهو ٿا ته اهي هتي ڇو آهن. هلندو رهڻ! جيئن توهان محسوس ڪيو هوندو، اسان جي MyClassڪلاس ۾ اڳ ۾ ئي ڪلاس ڊيٽا بابت معلومات ڏيکارڻ جو طريقو موجود آهي:
private void printData(){
       System.out.println(number + name);
   }
پر هن پروگرامر پنهنجي آڱرين جا نشان هتي به ڇڏيا. طريقي ۾ هڪ خانگي رسائي موڊيفائر آهي، ۽ اسان کي هر وقت ڊيٽا کي ڊسپلي ڪرڻ لاء پنهنجو ڪوڊ لکڻو پوندو. ڇا هڪ گندو. اسان جو عڪس ڪيڏانهن ويو؟ ھيٺ ڏنل فنڪشن لکو:
public static void printData(Object myClass){
   try {
       Method method = myClass.getClass().getDeclaredMethod("printData");
       method.setAccessible(true);
       method.invoke(myClass);
   } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
       e.printStackTrace();
   }
}
هتي جو طريقو ساڳيو آهي جيڪو هڪ فيلڊ کي ٻيهر حاصل ڪرڻ لاء استعمال ڪيو ويو آهي. اسان نالي سان گهربل طريقي تائين رسائي ڪريون ٿا ۽ ان تائين رسائي ڏيو. ۽ Methodاعتراض تي اسان کي invoke(Object, Args)طريقو سڏين ٿا، جتي Objectپڻ ڪلاس جو هڪ مثال آهي MyClass. Argsطريقن جا دليل آهن، جيتوڻيڪ اسان وٽ ڪو به نه آهي. هاڻي اسان printDataمعلومات کي ڊسپلي ڪرڻ لاء فنڪشن استعمال ڪندا آهيون:
public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; //?
   printData(myClass); // Output: 0default
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       field.set(myClass, (String) "new value");
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   printData(myClass);// Output: 0new value
}
هري! هاڻي اسان کي ڪلاس جي نجي طريقي تائين رسائي آهي. پر ڇا ڪجي جيڪڏهن طريقي ۾ دليل آهن، ۽ ڇو تعمير ڪندڙ تبصرو ڪيو ويو آهي؟ هر شي پنهنجي مقرر وقت ۾. اهو شروع ۾ وصف مان واضح آهي ته عڪاسي توهان کي رن ٽائم تي ڪلاس جا مثال ٺاهڻ جي اجازت ڏئي ٿي (جڏهن ته پروگرام هلي رهيو آهي)! اسان ڪلاس جو پورو نالو استعمال ڪندي هڪ اعتراض ٺاهي سگهون ٿا. ڪلاس جو پورو نالو ڪلاس جو نالو آھي، بشمول ان جي پيڪيج جو رستو .
Reflection API: Reflection.  جاوا جي اونداهي پاسي - 2
منهنجي پيڪيج جي ترتيب ۾، MyClass جو پورو نالو "reflection.MyClass" هوندو. ڪلاس جو نالو سکڻ جو ھڪڙو سادو طريقو پڻ آھي (ڪلاس جو نالو اسٽرنگ طور واپس ڪريو):
MyClass.class.getName()
اچو ته جاوا ريفريڪشن استعمال ڪريون ڪلاس جو مثال بڻائڻ لاءِ:
public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       myClass = (MyClass) clazz.newInstance();
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(myClass); // Output: created object reflection.MyClass@60e53b93
}
جڏهن هڪ جاوا ايپليڪيشن شروع ٿئي ٿي، نه سڀئي ڪلاس JVM ۾ لوڊ ڪيا ويا آهن. جيڪڏهن توهان جو ڪوڊ ڪلاس جو حوالو نٿو ڏئي MyClass، پوء ClassLoader، جيڪو JVM ۾ ڪلاس لوڊ ڪرڻ جو ذميوار آهي، ڪڏهن به ڪلاس لوڊ نه ڪندو. ان جو مطلب اهو آهي ته توهان کي ان کي لوڊ ڪرڻ لاء مجبور ڪرڻو پوندو ClassLoader۽ متغير جي صورت ۾ هڪ طبقي وضاحت حاصل ڪريو Class. اهو ئي سبب آهي ته اسان وٽ forName(String)طريقو آهي، جتي Stringڪلاس جو نالو آهي جنهن جي وضاحت اسان کي ضرورت آهي. اعتراض حاصل ڪرڻ کان پوء Сlass، طريقي کي ڪال ڪرڻ سان newInstance()ھڪڙي شئي واپس ايندي Objectجيڪا وضاحت استعمال ڪندي ٺاھي وئي. اهو سڀ ڪجهه ڇڏي ويو آهي هن اعتراض کي اسان جي MyClassطبقي کي فراهم ڪرڻ لاء. عمده! اهو ڏکيو هو، پر سمجھڻ وارو، مون کي اميد آهي. هاڻي اسان لفظي طور تي هڪ لڪير ۾ هڪ طبقي جو مثال ٺاهي سگهون ٿا! بدقسمتي سان، بيان ڪيل طريقي سان صرف ڊفالٽ تعمير ڪندڙ سان ڪم ڪندو (بغير پيٽرولر). توهان ڪئين طريقي سان سڏيندا آهيو طريقن ۽ تعمير ڪندڙن کي پيرا ميٽرن سان؟ اھو وقت آھي اسان جي ٺاھيندڙ کي غير تبصرو ڪرڻ جو. جيئن توقع ڪئي وئي، newInstance()ڊفالٽ تعمير ڪندڙ کي ڳولي نه سگھندو، ۽ وڌيڪ ڪم نه ڪندو. اچو ته ٻيهر لکون ڪلاس انسٽيٽيشن:
public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       Class[] params = {int.class, String.class};
       myClass = (MyClass) clazz.getConstructor(params).newInstance(1, "default2");
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
       e.printStackTrace();
   }
   System.out.println(myClass);// Output: created object reflection.MyClass@60e53b93
}
طريقي getConstructors()کي سڏيو وڃي ڪلاس جي تعريف تي ڪلاس تعمير ڪندڙ حاصل ڪرڻ لاء، ۽ پوء getParameterTypes()سڏيو وڃي هڪ تعمير ڪندڙ جي پيٽرولر حاصل ڪرڻ لاء:
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
   Class[] paramTypes = constructor.getParameterTypes();
   for (Class paramType : paramTypes) {
       System.out.print(paramType.getName() + " ");
   }
   System.out.println();
}
اهو اسان سڀني کي تعمير ڪندڙ ۽ انهن جي پيٽرولر حاصل ڪري ٿو. منهنجي مثال ۾، مان حوالو ڏيان ٿو هڪ مخصوص تعمير ڪندڙ سان مخصوص، اڳ ڄاڻايل پيٽرولن سان. ۽ هن تعمير ڪندڙ کي سڏڻ لاء، اسان اهو newInstanceطريقو استعمال ڪندا آهيون، جنهن ۾ اسان انهن پيٽرولن جي قيمتن کي پاس ڪندا آهيون. اهو ساڳيو ٿيندو جڏهن invokeڪال طريقن کي استعمال ڪندي. اهو سوال پيدا ڪري ٿو: جڏهن تعمير ڪندڙن کي موٽڻ جي ذريعي سڏڻ ڪم ۾ اچي ٿو؟ جيئن اڳ ۾ ئي ذڪر ڪيو ويو آهي، جديد جاوا ٽيڪنالاجيون حاصل نه ٿيون ڪري سگهن بغير Java Reflection API. مثال طور، Dependency Injection (DI)، جيڪو طريقن ۽ تعمير ڪندڙن جي عڪاسي سان تشريح کي گڏ ڪري ٿو ته جيئن Android ڊولپمينٽ لاءِ مشهور ڊارر لائبريري ٺاهي. هن آرٽيڪل کي پڙهڻ کان پوء، توهان اعتماد سان سمجهي سگهو ٿا پاڻ کي تعليم يافته جاوا ريفليشن API جي طريقن سان. اهي ريفريڪشن کي جاوا جي اونداهي پاسي نه ٿا سڏين. اهو مڪمل طور تي OOP پيراگراف کي ٽوڙي ٿو. جاوا ۾، encapsulation لڪائيندو آهي ۽ ٻين جي رسائي کي ڪجهه پروگرام جي اجزاء تائين محدود ڪري ٿو. جڏهن اسان پرائيويٽ موڊيفائر استعمال ڪريون ٿا، اسان جو ارادو آهي ته ان فيلڊ کي صرف ان طبقي ۾ پهچايو وڃي جتي اهو موجود آهي. ۽ اسان هن اصول جي بنياد تي پروگرام جي ايندڙ فن تعمير کي ٺاهيو. هن آرٽيڪل ۾، اسان ڏٺو آهي ته توهان ڪيئن استعمال ڪري سگهو ٿا عڪاسي کي پنهنجي رستي تي زور ڏيڻ لاءِ. تخليقي ڊيزائن جو نمونو سنگلٽن هن جو هڪ سٺو مثال آهي آرڪيٽيڪچرل حل جي طور تي. بنيادي خيال اهو آهي ته هڪ طبقو جيڪو هن نموني تي عمل ڪري ٿو، صرف هڪ مثال هوندو سڄي پروگرام جي عمل دوران. اهو مڪمل ڪيو ويو آهي شامل ڪندي پرائيويٽ رسائي موڊيفائر کي ڊفالٽ تعمير ڪندڙ ڏانهن. ۽ اهو تمام خراب ٿيندو جيڪڏهن ڪو پروگرامر عڪاسي استعمال ڪري ته اهڙن ڪلاسن جا وڌيڪ مثال ٺاهي. رستي ۾، مون تازو ٻڌو هڪ همراهه هڪ تمام دلچسپ سوال پڇي ٿو: ڇا هڪ ڪلاس جيڪو سنگلٽن جي نموني کي لاڳو ڪري ٿو وراثت ٿي سگهي ٿو؟ ڇا اهو ٿي سگهي ٿو ته، هن معاملي ۾، عڪاسي به بي طاقت هجي ها؟ ھيٺ ڏنل تبصرن ۾ مضمون ۽ توھان جي جواب بابت پنھنجي راءِ ڏيو، ۽ اتي پنھنجا پنهنجا سوال پڇو!

وڌيڪ پڙهڻ:

تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION