मुझे प्रतिबिंब का उपयोग क्यों करना चाहिए?


29

मैं जावा के लिए नया हूं; अपने अध्ययनों के माध्यम से, मैंने पढ़ा कि प्रतिबिंब का उपयोग कक्षाओं और विधियों को आह्वान करने के लिए किया जाता है, और यह जानने के लिए कि कौन से तरीकों को लागू किया जाता है या नहीं।

मुझे प्रतिबिंब का उपयोग कब करना चाहिए, और प्रतिबिंब और तात्कालिक वस्तुओं का उपयोग करने और पारंपरिक तरीकों को कॉल करने के बीच क्या अंतर है?


5
बाहर की जाँच करें stackoverflow.com/questions/37628/…
जलयान

10
कृपया पोस्ट करने से पहले अपने शोध को साझा करें। StackExchange (@Jalayn के रूप में विख्यात) और प्रतिबिंब के बारे में सामान्य रूप से वेब पर बहुत सारी सामग्री है। मैं आपको सुझाव देता हूं कि उदाहरण पर जावा ट्यूटोरियल को पढ़ें और उसके बाद वापस आएं यदि आपके पास कोई और ठोस सवाल है।
पेटर टॉर्क

1
वहां एक लाख की ठगी हुई है।
डेडएमजी

3
कुछ से अधिक पेशेवर प्रोग्रामर जवाब देंगे "जितना संभव हो उतना कम, शायद कभी भी नहीं।"
रॉस पैटरसन

जवाबों:


38
  • परावर्तन केवल उनके नाम से पुकारने के तरीकों की तुलना में बहुत धीमा है, क्योंकि इसे केवल पूर्व-निर्धारित पते और स्थिरांक का उपयोग करने के बजाय बायटेकोड में मेटाडेटा का निरीक्षण करना है।

  • प्रतिबिंब भी अधिक शक्तिशाली है: आप एक protectedया finalसदस्य की परिभाषा को पुनः प्राप्त कर सकते हैं , सुरक्षा को हटा सकते हैं और इसे हेरफेर कर सकते हैं जैसे कि इसे परिवर्तनशील घोषित किया गया था! जाहिर है कि यह कई गारंटी देता है कि भाषा आपके कार्यक्रमों के लिए सामान्य रूप से गारंटी देती है और बहुत, बहुत खतरनाक हो सकती है।

और यह बहुत स्पष्ट करता है कि इसका उपयोग कब करना है। आमतौर पर, नहीं। यदि आप किसी विधि को कॉल करना चाहते हैं, तो बस कॉल करें। यदि आप किसी सदस्य को उत्परिवर्तित करना चाहते हैं, तो बस संकलन की पीठ के पीछे जाने के बजाय इसे परिवर्तनशील घोषित करें।

प्रतिबिंब का एक उपयोगी वास्तविक दुनिया उपयोग यह है कि जब एक रूपरेखा लिखी जाती है जिसमें उपयोगकर्ता-परिभाषित कक्षाओं के साथ हस्तक्षेप करना होता है, जहां फ्रेमवर्क लेखक को यह नहीं पता होता है कि सदस्य (या यहां तक ​​कि कक्षाएं) क्या होंगे। परावर्तन उन्हें अग्रिम में जाने बिना किसी भी वर्ग से निपटने की अनुमति देता है। उदाहरण के लिए, मुझे नहीं लगता कि प्रतिबिंब के बिना एक जटिल पहलू-उन्मुख पुस्तकालय लिखना संभव होगा।

एक अन्य उदाहरण के रूप में, जुनिट ने एक तुच्छ बिट का उपयोग किया था: यह आपकी कक्षा में सभी तरीकों को बताता है, मानता है कि उन सभी testXXXको परीक्षण विधि कहा जाता है , और केवल उन पर अमल करता है। लेकिन अब इसके बजाय एनोटेशन के साथ बेहतर किया जा सकता है, और वास्तव में JUnit 4 के बजाय बड़े पैमाने पर एनोटेशन में चले गए हैं।


6
"अधिक शक्तिशाली" देखभाल की आवश्यकता है। ट्यूरिंग पूर्णता प्राप्त करने के लिए आपको प्रतिबिंब की आवश्यकता नहीं है, इसलिए किसी भी संगणना को कभी भी प्रतिबिंब की आवश्यकता नहीं होती है। बेशक ट्यूरिंग पूरी तरह से बिजली के अन्य प्रकारों के बारे में कुछ नहीं कहती है, जैसे कि I / O क्षमताएं और निश्चित रूप से, प्रतिबिंब।
स्टीव

1
परावर्तन आवश्यक रूप से "बहुत धीमा" नहीं है। आप डायरेक्ट कॉलिंग रैपर बायटेकोड उत्पन्न करने के लिए एक बार प्रतिबिंब का उपयोग कर सकते हैं।
एसके-तर्क

2
जरूरत पड़ने पर आप इसे जान जाएंगे। मैं अक्सर सोचता था कि (भाषा पीढ़ी के बाहर) किसी को इसकी आवश्यकता क्यों होगी। फिर, अचानक, मैंने किया ... माता-पिता / बाल श्रृंखलाओं को ऊपर और नीचे करने के लिए चलना पड़ा जब मैंने अपने बनाए हुए सिस्टमों में से किसी एक में पॉप करने के लिए पैनल को अन्य डेटा से पैनल / पोक किया।
ब्रायन नॉबलुच

@ एसके-लॉजिक: वास्तव में बायटेकोड उत्पन्न करने के लिए आपको बिल्कुल भी प्रतिबिंब की आवश्यकता नहीं है (वास्तव में प्रतिबिंब में बायटेकोड संचय के लिए कोई एपीआई नहीं है!)।
जोआचिम सॉर

1
@JoachimSauer, निश्चित रूप से, लेकिन आपको इस उत्पन्न बायोटेक को लोड करने के लिए एक प्रतिबिंब एपीआई की आवश्यकता होगी ।
एसके-तर्क

15

मैं एक बार आपकी तरह था, मैं प्रतिबिंब के बारे में बहुत कुछ नहीं जानता था - फिर भी नहीं - लेकिन मैंने एक बार इसका उपयोग किया था।

मेरे पास दो आंतरिक वर्गों के साथ एक वर्ग था, और प्रत्येक कक्षा में बहुत सारी विधियां थीं।

मुझे आंतरिक कक्षा में सभी विधियों को लागू करने की आवश्यकता थी, और उन्हें मैन्युअल रूप से लागू करना बहुत अधिक काम होगा।

परावर्तन के प्रयोग से, मैं इन सभी विधियों को कोड की सिर्फ 2-3 पंक्तियों में ही लागू कर सकता था, बजाय कि विधियों की संख्या के।


4
क्यों होता है पतन?
महमूद होसम

1
प्रस्तावना के लिए उत्थान
दिमित्री

1
@ ममौदहॉसम शायद सबसे अच्छा अभ्यास नहीं है, लेकिन आपका जवाब एक महत्वपूर्ण रणनीति को दिखाता है जिसे कोई भी तैनात कर सकता है।
ankush981

13

मैं तीन समूहों में प्रतिबिंब का उपयोग करता हूँ:

  1. मनमानी करने वाली कक्षाओं को त्वरित करना। उदाहरण के लिए, एक निर्भरता इंजेक्शन ढांचे में, आप शायद यह घोषणा करते हैं कि इंटरफ़ेस ThingDoer वर्ग NetworkThingDoer द्वारा कार्यान्वित किया जाता है। फ्रेमवर्क तब NetworkThingDoer के निर्माता को ढूंढेगा और उसे इंस्टेंट करेगा।
  2. किसी अन्य प्रारूप में मार्शलिंग और अनारक्षित करना। उदाहरण के लिए, गेटर्स और सेटिंग्स के साथ किसी ऑब्जेक्ट को मैप करना जो JSON के सेम कन्वेंशन का अनुसरण करता है और फिर से वापस आता है। कोड वास्तव में खेतों या विधियों के नाम नहीं जानता है, यह सिर्फ कक्षा की जांच करता है।
  3. एक कक्षा को पुनर्निर्देशन की परत में लपेटना (शायद वह सूची वास्तव में भरी हुई नहीं है, लेकिन किसी चीज़ के लिए केवल एक संकेतक है जो इसे डेटाबेस से प्राप्त करना जानता है) या पूरी तरह से कक्षा को रोकना (jock एक सिंथेटिक वर्ग बनाएगा जो एक इंटरफ़ेस को लागू करता है परीक्षण के प्रयोजनों के लिए)।

यह StackExchange पर मैंने पाया प्रतिबिंब का सबसे अच्छा विवरण है। अधिकांश उत्तर दोहराते हैं कि जावा ट्रेल क्या कहता है (जो "आप इन सभी गुणों को एक्सेस कर सकते हैं", लेकिन आप ऐसा क्यों करेंगे), प्रतिबिंब के साथ उन चीजों को करने के उदाहरण प्रदान करें जो बिना करना बहुत आसान है, या कैसे वसंत के बारे में कुछ अस्पष्ट उत्तर दें इसका उपयोग करता है। यह उत्तर वास्तव में तीन वैध उदाहरण देता है कि बिना प्रतिबिंब के JVM द्वारा आसानी से काम किया जा सकता है। धन्यवाद!
ndm13

3

प्रतिबिंब एक प्रोग्राम को कोड के साथ काम करने की अनुमति देता है जो मौजूद नहीं हो सकता है और विश्वसनीय तरीके से ऐसा कर सकता है।

"नॉर्मल कोड" में स्निपेट होते हैं जैसे URLConnection c = nullकि उसकी सरासर उपस्थिति के कारण क्लास लोडर इस क्लास को लोड करने के हिस्से के रूप में URLConnection क्लास को लोड करता है, ClassNotFound अपवाद और बाहर निकलने पर।

प्रतिबिंब आपको उन वर्गों पर उनके नाम के आधार पर कक्षाओं को लोड करने और वास्तविक गुणों को लॉन्च करने से पहले विभिन्न गुणों (उन्हें अपने नियंत्रण से बाहर कई संस्करणों के लिए उपयोगी) के लिए परीक्षण करने की अनुमति देता है। एक विशिष्ट उदाहरण ओएस एक्स विशिष्ट कोड है जिसका उपयोग जावा कार्यक्रमों को ओएस एक्स के तहत मूल रूप में देखने के लिए किया जाता है, जो अन्य प्लेटफार्मों पर मौजूद नहीं हैं।


2

मौलिक रूप से, प्रतिबिंब का अर्थ है डेटा के रूप में अपने प्रोग्राम के कोड का उपयोग करना।

इसलिए, प्रतिबिंब का उपयोग करना एक अच्छा विचार हो सकता है जब आपके प्रोग्राम का कोड डेटा का एक उपयोगी स्रोत है। (लेकिन ट्रेड-ऑफ हैं, इसलिए यह हमेशा एक अच्छा विचार नहीं हो सकता है।)

उदाहरण के लिए, एक साधारण वर्ग पर विचार करें:

public class Foo {
  public int value;
  public string anotherValue;
}

और आप इससे XML उत्पन्न करना चाहते हैं। आप XML बनाने के लिए कोड लिख सकते हैं:

public XmlNode generateXml(Foo foo) {
  XmlElement root = new XmlElement("Foo");
  XmlElement valueElement = new XmlElement("value");
  valueElement.add(new XmlText(Integer.toString(foo.value)));
  root.add(valueElement);
  XmlElement anotherValueElement = new XmlElement("anotherValue");
  anotherValueElement.add(new XmlText(foo.anotherValue));
  root.add(anotherValueElement);
  return root;
}

लेकिन यह बहुत सारे बॉयलरप्लेट कोड है, और हर बार जब आप क्लास बदलते हैं, तो आपको कोड अपडेट करना होगा। वास्तव में, आप वर्णन कर सकते हैं कि यह कोड क्या करता है

  • वर्ग के नाम के साथ एक XML तत्व बनाएँ
  • वर्ग की प्रत्येक संपत्ति के लिए
    • संपत्ति के नाम के साथ एक XML तत्व बनाएं
    • संपत्ति का मूल्य XML तत्व में डालें
    • XML तत्व को रूट में जोड़ें

यह एक एल्गोरिथ्म है, और एल्गोरिथ्म का इनपुट वर्ग है: हमें इसके नाम, और इसके गुणों के नाम, प्रकार और मूल्यों की आवश्यकता है। यह वह जगह है जहां प्रतिबिंब आता है: यह आपको इस जानकारी तक पहुंच प्रदान करता है। जावा आपको Classकक्षा के तरीकों का उपयोग करके प्रकारों का निरीक्षण करने की अनुमति देता है ।

कुछ और उपयोग के मामले:

  • एक वेबसर्वर में URL को एक क्लास के मेथड नेम, और URL के मेथड तर्कों के आधार पर परिभाषित करते हैं
  • एक वर्ग की संरचना को एक ग्राफ़िकल टाइप परिभाषा में परिवर्तित करें
  • एक वर्ग के हर तरीके को कॉल करें जिसका नाम यूनिट टेस्ट केस के रूप में "टेस्ट" से शुरू होता है

हालांकि, पूर्ण प्रतिबिंब का मतलब न केवल मौजूदा कोड को देखना है (जो अपने आप में "आत्मनिरीक्षण" के रूप में जाना जाता है), बल्कि कोड को संशोधित या संशोधित भी कर सकता है। इसके लिए जावा में दो प्रमुख उपयोग के मामले हैं: प्रॉक्सी और मोक्स।

मान लीजिए कि आपके पास एक इंटरफ़ेस है:

public interface Froobnicator {
  void froobnicateFruits(List<Fruit> fruits);
  void froobnicateFuel(Fuel fuel);
  // lots of other things to froobnicate
}

और आपके पास एक कार्यान्वयन है जो कुछ दिलचस्प करता है:

public class PowerFroobnicator implements Froobnicator {
  // awesome implementations
}

और वास्तव में आपके पास एक दूसरा कार्यान्वयन भी है:

public class EnergySaverFroobnicator implements Froobnicator {
  // efficient implementations
}

अब आप कुछ लॉग आउटपुट भी चाहते हैं; जब भी कोई विधि कहा जाता है आप बस एक लॉग संदेश चाहते हैं। आप स्पष्ट रूप से हर विधि में लॉग आउटपुट जोड़ सकते हैं, लेकिन यह कष्टप्रद होगा, और आपको इसे दो बार करना होगा; प्रत्येक कार्यान्वयन के लिए एक बार। (इसलिए और भी जब आप अधिक कार्यान्वयन जोड़ते हैं।)

इसके बजाय, आप एक प्रॉक्सी लिख सकते हैं:

public class LoggingFroobnicator implements Froobnicator {
  private Logger logger;
  private Froobnicator inner;

  // constructor that sets those two

  public void froobnicateFruits(List<Fruit> fruits) {
    logger.logDebug("froobnicateFruits called");
    inner.froobnicateFruits(fruits);
  }

  public void froobnicateFuel(Fuel fuel) {
    logger.logDebug("froobnicateFuel( called");
    inner.froobnicateFuel(fuel);
  }
  // lots of other things to froobnicate
}

फिर, हालांकि, एक दोहरावदार पैटर्न है जिसे एक एल्गोरिथ्म द्वारा वर्णित किया जा सकता है:

  • एक लकड़हारा प्रॉक्सी एक वर्ग है जो एक इंटरफ़ेस लागू करता है
  • इसमें एक कंस्ट्रक्टर है जो इंटरफ़ेस का एक और कार्यान्वयन और एक लकड़हारा लेता है
  • इंटरफ़ेस में हर विधि के लिए
    • कार्यान्वयन एक संदेश "$ methodname" कहा जाता है
    • और फिर सभी तर्कों के साथ गुजरते हुए, आंतरिक इंटरफ़ेस पर समान विधि को कॉल करता है

और इस एल्गोरिथ्म का इनपुट इंटरफ़ेस परिभाषा है।

प्रतिबिंब आपको इस एल्गोरिथ्म का उपयोग करके एक नए वर्ग को परिभाषित करने की अनुमति देता है। जावा आपको java.lang.reflect.Proxyकक्षा के तरीकों का उपयोग करके ऐसा करने की अनुमति देता है , और ऐसे पुस्तकालय हैं जो आपको और भी अधिक शक्ति प्रदान करते हैं।

तो प्रतिबिंब के डाउनसाइड क्या हैं?

  • आपका कोड समझना कठिन हो जाता है। आप अपने कोड के ठोस प्रभावों से हटाए गए अमूर्त स्तर का एक स्तर हैं।
  • आपका कोड डीबग करना कठिन हो जाता है। विशेष रूप से कोड-जनरेटिंग लाइब्रेरी के साथ, निष्पादित कोड वह कोड नहीं हो सकता है जिसे आपने लिखा था, लेकिन आपके द्वारा उत्पन्न कोड, और डीबगर आपको उस कोड को दिखाने में सक्षम नहीं हो सकता है (या आपको ब्रेकपॉइंट लगाने देता है)।
  • आपका कोड धीमा हो जाता है। हार्ड-कोडिंग एक्सेस के बजाय गतिशील रूप से पढ़ने की जानकारी और उनके रनटाइम हैंडल द्वारा खेतों तक पहुंच धीमी है। डिबग करने के लिए और भी कठिन होने की कीमत पर डायनामिक कोड जेनरेशन इस प्रभाव को कम कर सकता है।
  • आपका कोड अधिक नाजुक हो सकता है। डायनेमिक रिफ्लेक्शन एक्सेस कंपाइलर द्वारा टाइप-चेक नहीं किया जाता है, बल्कि रनटाइम में एरर्स को फेंकता है।

1

प्रतिबिंब आपके प्रोग्राम के कुछ हिस्सों को स्वचालित रूप से सिंक में रख सकता है, जहां पहले, आपको नए इंटरफेस का उपयोग करने के लिए अपने प्रोग्राम को मैन्युअल रूप से अपडेट करना होगा।


5
इस मामले में आपके द्वारा भुगतान की जाने वाली कीमत यह है कि आप IDE में कंपाइलर और रिफलेक्टर-सेफ्टी द्वारा टाइप-चेकिंग खो देते हैं। यह एक ऐसा व्यापार है जिसे मैं करने को तैयार नहीं हूँ।
Barend
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.