JavaRush /جاوا بلاگ /Random-UR /ریفلیکشن API۔ عکس. جاوا کا تاریک پہلو
Oleksandr Klymenko
سطح
Харків

ریفلیکشن API۔ عکس. جاوا کا تاریک پہلو

گروپ میں شائع ہوا۔
سلام، نوجوان پڈاون۔ اس مضمون میں میں آپ کو اس فورس کے بارے میں بتاؤں گا، جس کی طاقت جاوا پروگرامرز صرف بظاہر مایوس کن صورتحال میں استعمال کرتے ہیں۔ تو، جاوا کا تاریک پہلو ہے -Reflection API
ریفلیکشن API۔  عکس.  جاوا کا تاریک پہلو - 1
جاوا میں ریفلیکشن جاوا ریفلیکشن API کا استعمال کرتے ہوئے کیا جاتا ہے۔ یہ عکاسی کیا ہے؟ ایک مختصر اور قطعی تعریف ہے جو انٹرنیٹ پر بھی مقبول ہے۔ ریفلیکشن (Late Latin reflexio سے - واپس جانا) پروگرام کے عمل کے دوران اس کے بارے میں ڈیٹا کا مطالعہ کرنے کا ایک طریقہ کار ہے۔ عکاسی آپ کو فیلڈز، طریقوں اور کلاس کنسٹرکٹرز کے بارے میں معلومات کی جانچ کرنے کی اجازت دیتی ہے۔ عکاسی کا طریقہ کار خود آپ کو ان اقسام پر کارروائی کرنے کی اجازت دیتا ہے جو تالیف کے دوران غائب تھیں، لیکن پروگرام کے عمل کے دوران ظاہر ہوئیں۔ عکاسی اور غلطیوں کی اطلاع دینے کے لیے منطقی طور پر مربوط ماڈل کی موجودگی درست ڈائنامک کوڈ بنانا ممکن بناتی ہے۔ دوسرے لفظوں میں، یہ سمجھنا کہ جاوا میں عکاسی کیسے کام کرتی ہے آپ کے لیے بہت سے حیرت انگیز مواقع کھولتی ہے۔ آپ کلاسوں اور ان کے اجزاء کو لفظی طور پر جگا سکتے ہیں۔
ریفلیکشن API۔  عکس.  جاوا کا تاریک پہلو - 2
یہاں ایک بنیادی فہرست ہے جو عکاسی کی اجازت دیتا ہے:
  • کسی چیز کی کلاس کا پتہ لگانا/تعین کرنا؛
  • کلاس موڈیفائرز، فیلڈز، طریقے، مستقل، کنسٹرکٹرز اور سپر کلاسز کے بارے میں معلومات حاصل کریں۔
  • معلوم کریں کہ کون سے طریقے نافذ شدہ انٹرفیس/انٹرفیس سے تعلق رکھتے ہیں۔
  • کلاس کی ایک مثال بنائیں، اور کلاس کا نام اس وقت تک نامعلوم ہے جب تک کہ پروگرام پر عمل نہ ہو جائے۔
  • نام سے کسی آبجیکٹ فیلڈ کی ویلیو حاصل کریں اور سیٹ کریں۔
  • کسی چیز کے طریقہ کو نام سے کال کریں۔
ریفلیکشن تقریباً تمام جدید جاوا ٹیکنالوجیز میں استعمال ہوتا ہے۔ یہ تصور کرنا مشکل ہے کہ آیا جاوا ایک پلیٹ فارم کے طور پر بغیر کسی عکاسی کے اتنی زبردست اپنائیت حاصل کر سکتا تھا۔ غالباً میں ایسا نہیں کر سکا۔ آپ عکاسی کے عمومی نظریاتی خیال سے واقف ہو چکے ہیں، اب آئیے اس کے عملی اطلاق کی طرف آتے ہیں! ہم 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 for the' کی غیر موجودگی کو دیکھا 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۔ اگر ہمارے پاس اچانک 'a' نہیں ہے تو setter، ہم نام فیلڈ کے لیے ایک نئی قدر سیٹ کرنے کے لیے طریقہ استعمال کر سکتے ہیں 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۔
ریفلیکشن 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کال کرنے کے طریقوں کے لیے بھی ایسا ہی ہوگا ۔ سوال یہ پیدا ہوتا ہے: کنسٹرکٹرز کی عکاسی کہاں مفید ہو سکتی ہے؟ جدید جاوا ٹیکنالوجیز، جیسا کہ شروع میں ذکر کیا گیا ہے، Reflection API کے بغیر نہیں کر سکتے۔ مثال کے طور پر، DI (Dependency Injection)، جہاں طریقوں اور کنسٹرکٹرز کی عکاسی کے ساتھ مل کر تشریحات ڈیگر لائبریری کی تشکیل کرتی ہیں، جو اینڈرائیڈ کی ترقی میں مقبول ہے۔ اس مضمون کو پڑھنے کے بعد، آپ اعتماد کے ساتھ اپنے آپ کو Reflection API کے میکانزم میں روشن خیال کر سکتے ہیں۔ یہ کچھ بھی نہیں ہے کہ عکاسی کو جاوا کا تاریک پہلو کہا جاتا ہے۔ یہ مکمل طور پر OOP پیراڈائم کو توڑ دیتا ہے۔ جاوا میں، encapsulation کچھ پروگرام کے اجزاء کو چھپانے اور دوسروں تک رسائی کو محدود کرنے کا کام کرتا ہے۔ پرائیویٹ موڈیفائر کے استعمال سے ہمارا مطلب ہے کہ اس فیلڈ تک رسائی صرف اس کلاس کے اندر ہوگی جہاں یہ فیلڈ موجود ہے، اس کی بنیاد پر ہم پروگرام کا مزید فن تعمیر بناتے ہیں۔ اس مضمون میں، ہم نے دیکھا کہ آپ کہیں بھی جانے کے لیے عکاسی کا استعمال کیسے کر سکتے ہیں۔ آرکیٹیکچرل حل کی شکل میں ایک اچھی مثال تخلیقی ڈیزائن پیٹرن ہے - Singleton. اس کا بنیادی خیال یہ ہے کہ پروگرام کے پورے آپریشن کے دوران، اس ٹیمپلیٹ کو لاگو کرنے والی کلاس کے پاس صرف ایک کاپی ہونی چاہیے۔ یہ کنسٹرکٹر کے لیے ڈیفالٹ ایکسیس موڈیفائر کو پرائیویٹ پر سیٹ کرکے کیا جاتا ہے۔ اور یہ بہت برا ہو گا اگر کوئی پروگرامر اپنی عکاسی کے ساتھ ایسی کلاسیں بناتا ہے۔ ویسے، ایک بہت دلچسپ سوال ہے جو میں نے حال ہی میں اپنے ملازم سے سنا ہے: کیا ٹیمپلیٹ کو لاگو کرنے والی کلاس کے Singletonوارث ہو سکتے ہیں؟ کیا یہ ممکن ہے کہ اس معاملے میں عکاسی بھی بے اختیار ہو؟ مضمون پر اپنی رائے اور جواب کمنٹس میں لکھیں، اور اپنے سوالات بھی پوچھیں! ریفلیکشن API کی حقیقی طاقت رن ٹائم تشریحات کے ساتھ مل کر آتی ہے، جس کے بارے میں ہم شاید مستقبل کے مضمون میں جاوا کے تاریک پہلو کے بارے میں بات کریں گے۔ آپکی توجہ کا شکریہ!
تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION