JavaRush /جاوا بلاگ /Random-SD /Reflection API. عڪس. جاوا جي اونداهي پاسي
Oleksandr Klymenko
سطح
Харків

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

گروپ ۾ شايع ٿيل
سلام، نوجوان پاداوان. هن آرٽيڪل ۾ آئون توهان کي ان فورس بابت ٻڌايان ٿو، جنهن جي طاقت جاوا پروگرامر صرف هڪ بظاهر نا اميد صورتحال ۾ استعمال ڪندا آهن. تنهن ڪري، جاوا جي اونداهي طرف آهي -Reflection API
Reflection API.  عڪس.  جاوا جي اونداهي پاسي - 1
Java Reflection API استعمال ڪندي جاوا ۾ عڪس ڪيو ويندو آهي. هي عڪس ڇا آهي؟ هڪ مختصر ۽ صحيح وصف آهي جيڪا انٽرنيٽ تي پڻ مشهور آهي. Reflection (مرحوم لاطيني reflexio کان - واپس وڃڻ) هڪ ميکانيزم آهي جيڪو ان جي عمل دوران پروگرام بابت ڊيٽا جي مطالعي لاءِ. Reflection توهان کي اجازت ڏئي ٿو ته معلومات کي جانچڻ لاءِ فيلڊز، طريقن، ۽ ڪلاس جي تعمير ڪندڙ. عکاسي ميڪانيزم پاڻ توهان کي انهن قسمن کي پروسيس ڪرڻ جي اجازت ڏئي ٿو جيڪي تاليف دوران غائب آهن، پر پروگرام جي عمل دوران ظاهر ٿيندا آهن. عکاسي ۽ رپورٽنگ جي غلطي لاء منطقي طور تي مربوط ماڊل جي موجودگي کي درست متحرڪ ڪوڊ ٺاهڻ ممڪن بڻائي ٿو. ٻين لفظن ۾، سمجھڻ ته ڪيئن عکاسي جاوا ۾ ڪم ڪري ٿي توھان لاءِ ڪيترائي شاندار موقعا کولي ٿو. توهان لفظي طور تي طبقن ۽ انهن جي اجزاء کي ڇڪي سگهو ٿا.
Reflection API.  عڪس.  جاوا جي اونداهي پاسي - 2
هتي هڪ بنيادي فهرست آهي جنهن جي عڪاسي جي اجازت ڏئي ٿي:
  • ھڪڙي شئي جي درجي کي ڳولھيو / طئي ڪريو؛
  • ڪلاس ميڊيفائرز، فيلڊز، طريقا، مستقل، تعمير ڪندڙ ۽ سپر ڪلاسز بابت ڄاڻ حاصل ڪريو؛
  • معلوم ڪريو ته ڪهڙا طريقا لاڳو ٿيل انٽرفيس/انٽرفيس سان تعلق رکن ٿا؛
  • ھڪڙي ڪلاس جو ھڪڙو مثال ٺاھيو، ۽ ڪلاس جو نالو نامعلوم آھي جيستائين پروگرام تي عمل نه ڪيو وڃي؛
  • حاصل ڪريو ۽ نالي سان هڪ اعتراض فيلڊ جي قيمت مقرر ڪريو؛
  • نالي سان هڪ اعتراض جي طريقي کي ڪال ڪريو.
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);
   }
}
جيئن اسان ڏسي سگهون ٿا، اهو سڀ کان وڌيڪ عام طبقو آهي. پيرا ميٽرز سان ٺاھيندڙ ھڪڙي سبب لاءِ تبصرو ڪيو ويو آھي، اسان ان کي بعد ۾ واپس ڪنداسين. جيڪڏهن توهان ڪلاس جي مواد کي ويجهي کان ڏٺو، توهان شايد getter'a' جي غير موجودگي کي ڏٺو name. فيلڊ پاڻ nameهڪ رسائي موڊيفائر سان نشان لڳل آهي private؛ اسان ان کي ڪلاس کان ٻاهر تائين رسائي نٿا ڪري سگهون؛ =>اسان ان جي قيمت حاصل نٿا ڪري سگهون. ”پوءِ مسئلو ڇا آهي؟ - توهان بڌايو. " getterرسائي موڊيفائر کي شامل ڪريو يا تبديل ڪريو." ۽ توهان صحيح ٿا چئو، پر ڇا جيڪڏهن MyClassاهو هڪ مرتب ٿيل آر آر لائبريري ۾ هجي يا ڪنهن ٻئي بند ماڊل ۾ بغير ايڊيٽنگ جي رسائي، ۽ عملي طور تي اهو تمام گهڻو ٿئي ٿو. ۽ ڪجهه بي پرواهه پروگرامر صرف لکڻ وساري ويٺا getter. اهو عڪس جي باري ۾ ياد ڪرڻ جو وقت آهي! اچو ته ڪوشش ڪريون ڪلاس جي privateميدان ۾ : nameMyClass
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()، هي طريقو اسان کي ڪلاس جا سڀ موجود فيلڊ واپس ڏيندو. اهو اسان لاءِ موزون نه آهي، ڇاڪاڻ ته اسان جي فيلڊ آهي private، ان ڪري اسان اهو طريقو استعمال ڪريون ٿا getDeclaredFields()، اهو طريقو پڻ ڪلاس فيلڊز جي هڪ صف کي واپس ڏئي ٿو، پر هاڻي ٻنهي private۽ protected. اسان جي صورتحال ۾، اسان ڄاڻون ٿا ته فيلڊ جو نالو جيڪو اسان جي فائدي ۾ آهي، ۽ اسان اهو طريقو استعمال ڪري سگهون ٿا getDeclaredField(String)، جتي Stringگهربل فيلڊ جو نالو آهي. نوٽ: getFields()۽ getDeclaredFields()والدين طبقي جا شعبا واپس نه ڏيو! عظيم، اسان کي Field اسان جي لنڪ سان هڪ اعتراض مليو name. ڇاڪاڻ ته فيلڊ публичным(عوامي) نه هئي، ان سان ڪم ڪرڻ لاء رسائي ڏني وڃي. طريقو setAccessible(true)اسان کي ڪم جاري رکڻ جي اجازت ڏئي ٿو. هاڻي ميدان nameمڪمل طور تي اسان جي قبضي هيٺ آهي! get(Object)توھان ان جي قيمت حاصل ڪري سگھو ٿا اعتراض کي ڪال ڪندي Field، جتي Objectاسان جي ڪلاس جو ھڪڙو مثال آھي MyClass. اسان ان کي اڇلائي ڇڏيو String۽ ان کي اسان جي متغير کي تفويض ڪيو name. صورت ۾ اسان کي اوچتو نه آهي setter'a، اسان طريقي سان استعمال ڪري سگھون ٿا نالي جي فيلڊ لاء نئين قيمت مقرر ڪرڻ لاء set:
field.set(myClass, (String) "new value");
مبارڪون هجن! توهان صرف فڪر جي بنيادي ميکانيزم ۾ مهارت حاصل ڪئي آهي ۽ privateفيلڊ تائين رسائي حاصل ڪرڻ جي قابل هئا! بلاڪ تي ڌيان ڏيو try/catch۽ استثناء جي قسمن کي سنڀاليو. IDE پاڻ انهن جي لازمي موجودگي کي ظاهر ڪندو، پر انهن جو نالو اهو واضح ڪري ٿو ته اهي هتي ڇو آهن. اڳتي وڃو! جيئن توهان محسوس ڪيو هوندو، اسان وٽ MyClassاڳ ۾ ئي ڪلاس ڊيٽا بابت معلومات ڏيکارڻ جو طريقو آهي:
private void printData(){
       System.out.println(number + name);
   }
پر هن پروگرامر هتي به هڪ وراثت ڇڏي. طريقو رسائي موڊيفائر جي تحت آهي private، ۽ اسان کي هر وقت پنهنجو پاڻ کي آئوٽ ڪوڊ لکڻو پوندو. اهو ترتيب ۾ ناهي ته اسان جو عڪس ڪٿي آهي؟... اچو ته هيٺيون فنڪشن لکون:
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(Оbject, Args)، جتي Оbjectپڻ ڪلاس جو هڪ مثال آهي MyClass. Args- طريقو دليل - اسان جو ڪو به نه آهي. ھاڻي اسان معلومات کي ڊسپلي ڪرڻ لاء فنڪشن استعمال ڪندا آھيون printData:
public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; //?
   printData(myClass); // outout 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
}
هير، هاڻي اسان کي ڪلاس جي نجي طريقي تائين رسائي آهي. پر ڇا ٿيندو جيڪڏهن طريقو اڃا تائين دليل آهي، ۽ اتي هڪ تبصرو ٿيل تعمير ڪندڙ ڇو آهي؟ هر شيءِ جو پنهنجو وقت آهي. شروعات ۾ تعريف کان اهو واضح آهي ته عڪاسي توهان کي موڊ ۾ ڪلاس جا مثال ٺاهڻ جي اجازت ڏئي ٿي runtime(جڏهن ته پروگرام هلندي آهي)! اسان ان طبقي جي مڪمل طور تي قابليت واري نالي سان هڪ ڪلاس جو هڪ اعتراض ٺاهي سگهون ٿا. مڪمل طور تي قابل ڪلاس جو نالو ڪلاس جو نالو آھي، جنھن ۾ ان کي رستو ڏنو ويو آھي package.
Reflection API.  عڪس.  جاوا جي اونداهي پاسي - 3
منهنجي درجه بندي ۾، packageپورو نالو 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، ته پوءِ جيڪو به ذميوار آهي ڪلاسز کي JVM ۾ لوڊ ڪرڻ جو، ۽ اهو آهي ClassLoader، ان کي اتي ڪڏهن به لوڊ نه ڪندو. تنهن ڪري، اسان کي 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سڏڻ جي طريقن لاءِ. سوال پيدا ٿئي ٿو: تعمير ڪندڙن جي عکاس ڪالنگ ڪٿي مفيد ٿي سگهي ٿي؟ جديد جاوا ٽيڪنالاجيون، جيئن شروع ۾ ذڪر ڪيو ويو آهي، ريفليشن API کان سواءِ نٿا ڪري سگهن. مثال طور، DI (Dependency Injection)، جتي تشريحون ملن ٿيون طريقن جي عڪاسي ۽ ڪنسٽرڪٽرز ڊاگر لائبريري ٺاهي ٿي، جيڪا Android ڊولپمينٽ ۾ مشهور آهي. هن مضمون کي پڙهڻ کان پوء، توهان اعتماد سان سمجهي سگهو ٿا پاڻ کي روشن خيال ريفليشن API جي ميڪانيزم ۾. اهو ڪجھ به نه آهي ته عڪاسي کي جاوا جي اونداهي طرف سڏيو ويندو آهي. اهو مڪمل طور تي OOP پيراگراف کي ٽوڙي ٿو. جاوا ۾، encapsulation کي لڪائڻ ۽ محدود ڪرڻ جي خدمت ڪري ٿو ڪجهه پروگرام جي اجزاء جي رسائي ٻين تائين. پرائيويٽ موڊيفائر استعمال ڪرڻ سان اسان جو مطلب آهي ته هن فيلڊ تائين پهچ صرف ان طبقي جي اندر هوندي جتي هي فيلڊ موجود آهي، ان جي بنياد تي اسان پروگرام جو وڌيڪ فن تعمير ڪندا آهيون. هن آرٽيڪل ۾، اسان ڏٺو ته توهان ڪٿي به حاصل ڪرڻ لاء عڪس ڪيئن استعمال ڪري سگهو ٿا. هڪ آرڪيٽيڪچرل حل جي صورت ۾ هڪ سٺو مثال پيدا ٿيندڙ ڊيزائن جو نمونو آهي - Singleton. ان جو بنيادي خيال اهو آهي ته پروگرام جي پوري آپريشن دوران، هن ٽيمپليٽ کي لاڳو ڪندڙ طبقي وٽ صرف هڪ ڪاپي هجڻ گهرجي. اهو ڪيو ويندو آهي ڊفالٽ رسائي موڊيفائر کي ترتيب ڏيڻ لاءِ پرائيويٽ ۾. ۽ اهو تمام خراب ٿيندو جيڪڏهن ڪو پروگرامر پنهنجي عڪاسي سان اهڙا ڪلاس ٺاهي. رستي ۾، هڪ تمام دلچسپ سوال آهي جيڪو مون تازو پنهنجي ملازم کان ٻڌو آهي: ڇا هڪ طبقو جيڪو ٽيمپليٽ کي لاڳو ڪري ٿو، Singletonوارث ٿي سگهي ٿو؟ ڇا اهو ممڪن آهي ته هن معاملي ۾ به عڪاسي بي طاقت آهي؟ مضمون تي پنهنجا تاثرات ۽ جواب تبصرن ۾ لکو، ۽ پڻ پنھنجا سوال پڇو! Reflection API جي حقيقي طاقت رن ٽائم اينوٽيشنز سان ميلاپ ۾ اچي ٿي، جيڪا اسان شايد مستقبل جي مضمون ۾ جاوا جي اونداهي پاسي بابت ڳالهائينداسين. توهان جي توجه لاء مهرباني!
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION