जावा में जेनेरिक

जेनरिक सामान्य प्रोग्रामिंग की सुविधा है, जिसे 2004 में जावा प्लेटफ़ॉर्म, मानक संस्करण 5.0 के अन्दर जावा (प्रोग्रामिंग भाषा) में जोड़ा गया था। उन्हें संकलन-समय प्रकार की सुरक्षा प्रदान करते हुए विभिन्न प्रकार की वस्तुओं पर एक प्रकार या विधि को संचालित करने की अनुमति देने के लिए जावा के प्रकार प्रणाली का विस्तार करने के लिए डिज़ाइन किया गया था। पहलू संकलन-समय प्रकार की सुरक्षा पूरी तरह से प्राप्त नहीं की गई थी, क्योंकि 2016 में यह दिखाया गया था कि सभी स्थितियों में इसकी गारंटी नहीं है।

जावा संग्रह ढाँचा संग्रह उदाहरण में संग्रहीत वस्तुओं के प्रकार को निर्दिष्ट करने के लिए जेनेरिक का समर्थन करता है।

1998 में, गिलाद ब्राचा, मार्टिन ओडर्सकी, डेविड स्टाउटमायर और फिलिप वाडलर ने जेनेरिक जावा बनाया, जो सामान्य प्रकारों का समर्थन करने के लिए जावा भाषा का विस्तार था। जेनेरिक जावा को वाइल्डकार्ड (जावा) के साथ जावा में सम्मिलित किया गया था।

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

प्रेरणा
जावा कोड का निम्नलिखित ब्लॉक उस समस्या को दर्शाता है जो जेनेरिक का उपयोग न करने पर उपलब्ध होती है। सबसे पहले, यह प्रकार की  घोषित करता है। फिर, यह   में   जोड़ता है। अंत में, यह जोड़े गए   को पुनः प्राप्त करने का प्रयास करता है और इसे   में डालने का प्रयास करता है - तर्क में त्रुटि, क्योंकि सामान्यतः पूर्णांक में इच्छानुसार स्ट्रिंग डालना संभव नहीं है।

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

उपरोक्त कोड खंड को जेनेरिक का उपयोग करके निम्नानुसार फिर से लिखा जा सकता है:

कोण कोष्ठक के अन्दर टाइप पैरामीटर    को   (  के सामान्य   घटकों का वंशज) से गठित होने की घोषणा करता है। जेनेरिक के साथ, तीसरी पंक्ति को किसी विशेष प्रकार में डालना अब आवश्यक नहीं है, क्योंकि   के परिणाम को कंपाइलर द्वारा उत्पन्न कोड द्वारा   के रूप में परिभाषित किया गया है।

इस खंड की तीसरी पंक्ति में तार्किक दोष को संकलन-समय त्रुटि (J2SE 5.0 या बाद के संस्करण के साथ) के रूप में पहचाना जाएगा क्योंकि कंपाइलर यह पता लगाएगा कि    पूर्णांक के अतिरिक्त   लौटाता है। अधिक विस्तृत उदाहरण के लिए, संदर्भ देखें।

यहां पैकेज में इंटरफेस  और  की परिभाषा से छोटा सा अंश दिया गया है:

सामान्य वर्ग परिभाषाएँ
यहां सामान्य जावा क्लास का उदाहरण दिया गया है, जिसका उपयोग मानचित्र (कंप्यूटर विज्ञान) में व्यक्तिगत प्रविष्टियों (वैल्यू मैपिंग की कुंजी) को दर्शाने के लिए किया जा सकता है:

इस सामान्य वर्ग का उपयोग निम्नलिखित विधियों से किया जा सकता है, उदाहरण के लिए:

यह आउटपुट देता है:

सामान्य विधि परिभाषाएँ
उपरोक्त सामान्य वर्ग का उपयोग करते हुए सामान्य विधि का उदाहरण यहां दिया गया है: नोट: यदि हम पहले  को हटा दें, उपरोक्त विधि में, हमें संकलन त्रुटि मिलेगी (प्रतीक प्रकार नहीं मिल सका), क्योंकि यह प्रतीक की घोषणा का प्रतिनिधित्व करता है।

कई स्थितियों में, विधि के उपयोगकर्ता को प्रकार के मापदंडों को इंगित करने की आवश्यकता नहीं होती है, क्योंकि उनका अनुमान लगाया जा सकता है:

यदि आवश्यक हो तो पैरामीटर स्पष्ट रूप से जोड़े जा सकते हैं:

आदिम प्रकार के उपयोग की अनुमति नहीं है, और इसके अतिरिक्त ऑब्जेक्ट प्रकार (ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग) बॉक्सिंग संस्करणों का उपयोग किया जाना चाहिए:

दिए गए मापदंडों के आधार पर सामान्य विधि बनाने की भी संभावना है।

ऐसी स्थितियों में आप आदिम प्रकारों का भी उपयोग नहीं कर सकते, उदाहरण के लिए:

डायमंड ऑपरेटर
प्रकार के अनुमान के लिए धन्यवाद, जावा एसई 7 और इसके बाद के संस्करण प्रोग्रामर को कोण कोष्ठक की खाली जोड़ी (, जिसे डायमंड ऑपरेटर कहा जाता है) को कोण कोष्ठक की जोड़ी के लिए प्रतिस्थापित करने की अनुमति देता है जिसमें एक या अधिक प्रकार के पैरामीटर होते हैं जो पर्याप्त रूप से बंद संदर्भ का तात्पर्य है। इस प्रकार,  का उपयोग करके उपरोक्त कोड उदाहरण को इस प्रकार पुनः लिखा जा सकता है:

वाइल्डकार्ड टाइप करें
पैरामीटरयुक्त प्रकार के लिए एक प्रकार का तर्क किसी ठोस वर्ग या इंटरफ़ेस तक सीमित नहीं है। जावा पैरामीटरयुक्त प्रकारों के लिए प्रकार के तर्क के रूप में "टाइप वाइल्डकार्ड" के उपयोग की अनुमति देता है। वाइल्डकार्ड " " रूप में प्रकार के तर्क हैं; वैकल्पिक रूप से ऊपरी या निचली सीमा के साथ। यह देखते हुए कि वाइल्डकार्ड द्वारा दर्शाया गया स्पष्ट प्रकार अज्ञात है, उन विधियों के प्रकार पर प्रतिबंध लगाए जाते हैं जिन्हें किसी ऑब्जेक्ट पर बुलाया जा सकता है जो पैरामीटरयुक्त प्रकारों का उपयोग करता है।

यहां उदाहरण दिया गया है, जहां  के तत्व प्रकार को वाइल्डकार्ड द्वारा पैरामीटराइज़ किया गया है:

चूँकि हम नहीं जानते कि तत्व प्रकार  का क्या अर्थ है, हम इसमें ऑब्जेक्ट नहीं जोड़ सकते।   विधि   e> प्रकार के तर्क लेती है, जो कि   जेनेरिक इंटरफ़ेस का तत्व प्रकार है। जब वास्तविक प्रकार का तर्क   होता है, तो यह किसी अज्ञात प्रकार का प्रतिनिधित्व करता है।   विधि में हम जो भी विधि तर्क मान पास करते हैं वह इस अज्ञात प्रकार का उपप्रकार होना चाहिए। चूँकि हम नहीं जानते कि वह किस प्रकार का है, हम कुछ भी पारित नहीं कर सकते हैं। एकमात्र अपवाद शून्य है; जो हर प्रकार का सदस्य है।

किसी प्रकार के वाइल्डकार्ड की ऊपरी सीमा को निर्दिष्ट करने के लिए, कीवर्ड का उपयोग यह इंगित करने के लिए किया जाता है कि प्रकार तर्क बाउंडिंग क्लास का उपप्रकार है। तो  का अर्थ है कि दी गई सूची में कुछ अज्ञात प्रकार की वस्तुएं हैं जो   वर्ग का विस्तार करती हैं। उदाहरण के लिए, सूची   या   हो सकती है। सूची से किसी तत्व को पढ़ने पर   वापस आ जाएगी। फिर से, अशक्त तत्वों को जोड़ने की भी अनुमति है।

उपरोक्त वाइल्डकार्ड का उपयोग लचीलापन जोड़ता है क्योंकि ठोस प्रकार के तर्क के साथ किन्हीं दो पैरामीटरयुक्त प्रकारों के बीच कोई विरासत संबंध नहीं है। न तो  और न ही   दूसरे का उपप्रकार है; तथापि     का उपप्रकार है। इसलिए, कोई भी विधि जो   को पैरामीटर के रूप में लेती है,   के तर्क को स्वीकार नहीं करती है। यदि ऐसा होता, तो इसमें ऐसा   सम्मिलित करना संभव होता जो   नहीं है; जो प्रकार की सुरक्षा का उल्लंघन करता है। यहां उदाहरण दिया गया है जो दर्शाता है कि यदि     का उपप्रकार होता तो किस प्रकार प्रकार की सुरक्षा का उल्लंघन किया जाता:

वाइल्डकार्ड के साथ समाधान काम करता है क्योंकि यह उन परिचालनों की अनुमति नहीं देता है जो प्रकार की सुरक्षा का उल्लंघन करेंगे:

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

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

थ्रो क्लॉज़ में जेनरिक
चूँकि अपवाद स्वयं सामान्य नहीं हो सकते, सामान्य पैरामीटर थ्रो क्लॉज में दिखाई दे सकते हैं:

प्रकार मिटाने की समस्या
टाइप-शुद्धता के लिए संकलन-समय पर जेनेरिक की जाँच की जाती है। सामान्य प्रकार की जानकारी को प्रकार मिटाना नामक प्रक्रिया में हटा दिया जाता है। उदाहरण के लिए,  गैर-जेनेरिक  प्रकार में परिवर्तित कर दिया जाएगा, जिसमें सामान्यतः इच्छानुसार वस्तुएं सम्मिलित होती हैं। संकलन-समय जांच यह गारंटी देती है कि परिणामी कोड सही प्रकार का उपयोग करता है।

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

इस बिंदु को प्रदर्शित करते हुए, निम्नलिखित कोड समान आउटपुट देता है: प्रकार के क्षरण का अन्य प्रभाव यह है कि सामान्य वर्ग किसी भी तरह से, प्रत्यक्ष या अप्रत्यक्ष रूप से,  वर्ग का विस्तार नहीं कर सकता है:

इसके समर्थित न होने का कारण प्रकार का क्षरण है: टाइप इरेज़र के कारण, रनटाइम को पता नहीं चलेगा कि कौन सा कैच ब्लॉक निष्पादित करना है, इसलिए यह कंपाइलर द्वारा निषिद्ध है।

जावा जेनेरिक सी++ टेम्पलेट्स से भिन्न हैं। उपयोग किए गए पैरामीटर प्रकारों की संख्या की परवाह किए बिना, जावा जेनेरिक जेनेरिक वर्ग या फलन का केवल संकलित संस्करण उत्पन्न करते हैं। इसके अतिरिक्त, जावा रन-टाइम वातावरण को यह जानने की आवश्यकता नहीं है कि किस पैरामीटरयुक्त प्रकार का उपयोग किया जाता है क्योंकि प्रकार की जानकारी संकलन-समय पर मान्य होती है और संकलित कोड में सम्मिलित नहीं होती है। परिणामस्वरूप, पैरामीटरयुक्त प्रकार के जावा क्लास को इंस्टेंट करना असंभव है क्योंकि इंस्टेंटेशन के लिए कंस्ट्रक्टर को कॉल की आवश्यकता होती है, जो कि प्रकार अज्ञात होने पर अनुपलब्ध है।

उदाहरण के लिए, निम्नलिखित कोड संकलित नहीं किया जा सकता: चूँकि रनटाइम पर प्रति जेनेरिक क्लास की केवल प्रतिलिपि होती है, स्थैतिक चर को क्लास के सभी उदाहरणों के बीच साझा किया जाता है, चाहे उनके प्रकार के पैरामीटर कुछ भी हों। परिणामस्वरूप, प्रकार पैरामीटर का उपयोग स्थिर चर की घोषणा में या स्थिर विधियों में नहीं किया जा सकता है।

जावा SE5 से पहले लिखे गए प्रोग्रामों के साथ बैकवर्ड संगतता बनाए रखने के लिए जावा में टाइप इरेज़र प्रयुक्त किया गया था।

सरणी से अंतर
जावा में सरणियों (आदिम सरणियों और Object सरणियों दोनों) और जेनेरिक के बीच कई महत्वपूर्ण अंतर हैं। दो प्रमुख अंतर, अर्थात् विचरण और रीफिकेशन के संदर्भ में अंतर।

सहप्रसरण, प्रतिप्रसरण और अपरिवर्तन
जेनेरिक अपरिवर्तनीय हैं, जबकि सरणियाँ सहप्रसरण और प्रतिप्रसरण (कंप्यूटर विज्ञान) हैं। सरणियों जैसी गैर-जेनेरिक वस्तुओं की तुलना में जेनेरिक का उपयोग करने का यह लाभ है। विशेष रूप से, जेनरिक डेवलपर को कोड को ठीक करने के लिए विवश करने के लिए संकलन-समय अपवाद फेंककर रन टाइम अपवादों को रोकने में सहायता कर सकता है।

उदाहरण के लिए, यदि कोई डेवलपर Object[] ऑब्जेक्ट घोषित करता है और ऑब्जेक्ट को नए Long[]] ऑब्जेक्ट के रूप में इंस्टेंट करता है, तो कोई संकलन-समय अपवाद नहीं फेंका जाता है (क्योंकि एरे सहसंयोजक होते हैं)। इससे यह ग़लत धारणा बन सकती है कि कोड सही ढंग से लिखा गया है। चूँकि, यदि डेवलपर इस Long[] ऑब्जेक्ट में String जोड़ने का प्रयास करता है, तो प्रोग्राम ArrayStoreException फेंक देगा। यदि डेवलपर जेनेरिक का उपयोग करता है तो इस रन-टाइम अपवाद से पूरी तरह बचा जा सकता है।

यदि डेवलपर Collection ऑब्जेक्ट घोषित करता है और रिटर्न प्रकार ArrayList के साथ इस ऑब्जेक्ट का नया उदाहरण बनाता है, तो जावा कंपाइलर (सही ढंग से) असंगत प्रकारों की उपस्थिति को इंगित करने के लिए संकलन-समय अपवाद फेंक देगा (क्योंकि जेनेरिक हैं)। इसलिए, यह संभावित रन-टाइम अपवादों से बचा जाता है। इसके बजाय ArrayList ऑब्जेक्ट का उपयोग करके Collection का उदाहरण बनाकर इस समस्या को ठीक किया जा सकता है। जावा SE7 या बाद के संस्करणों का उपयोग करने वाले कोड के लिए, Collection को डायमंड ऑपरेटर का उपयोग करके ArrayList<> ऑब्जेक्ट के साथ त्वरित किया जा सकता है।

पुनःकरण
ऐरे को पुनरीक्षित किया जाता है, जिसका अर्थ है कि ऐरे ऑब्जेक्ट रन-टाइम पर अपने प्रकार की जानकारी को प्रयुक्त करता है, जबकि जावा में जेनेरिक को पुन: संशोधित नहीं किया जाता है।

अधिक औपचारिक रूप से कहें तो, जावा में सामान्य प्रकार वाली वस्तुएं गैर-शोधन योग्य प्रकार हैं। गैर-शोधन योग्य प्रकार वह प्रकार है, जिसका रन-टाइम पर प्रतिनिधित्व में संकलन-समय पर इसके प्रतिनिधित्व की तुलना में कम जानकारी होती है।

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

गैर-शोधन योग्य प्रकारों के उदाहरणों में List और List सम्मिलित हैं, जहाँ T सामान्य औपचारिक पैरामीटर है।

जेनेरिक पर प्रोजेक्ट
प्रोजेक्ट वल्लाह (जावा भाषा) जावा 10 से आगे के संभावित संस्करणों के लिए उत्तम जावा जेनेरिक और भाषा सुविधाओं को विकसित करने के लिए प्रायोगिक परियोजना है। संभावित संवर्द्धन में सम्मिलित हैं:
 * सामान्य विशेषज्ञता, उदाहरण List
 * रिफाइड जेनेरिक; रनटाइम पर वास्तविक प्रकार उपलब्ध कराना।

यह भी देखें

 * सामान्य प्रोग्रामिंग
 * टेम्प्लेट मेटाप्रोग्रामिंग
 * वाइल्डकार्ड (जावा)
 * सी# और जावा की तुलना
 * जावा और सी++ की तुलना