जवाबों:
मैं अपनी आवाज़ को शोर से जोड़ूंगा और चीजों को स्पष्ट करने पर एक क़दम उठाऊँगा:
List<Person> foo = new List<Person>();
और फिर संकलक आपको उन चीजों को डालने से रोक देगा Person
जो सूची में नहीं हैं ।
पर्दे के पीछे C # संकलक सिर्फ List<Person>
.NET dll फ़ाइल में डाल रहा है, लेकिन रनटाइम पर JIT संकलक जाता है और कोड का एक नया सेट बनाता है, जैसे कि आपने केवल लोगों को शामिल करने के लिए एक विशेष सूची वर्ग लिखा था - कुछ ऐसा ListOfPerson
।
इसका लाभ यह है कि यह वास्तव में तेज करता है। कोई कास्टिंग या कोई अन्य सामान नहीं है, और क्योंकि dll में यह जानकारी है कि यह एक सूची है Person
, अन्य कोड जो बाद में प्रतिबिंब का उपयोग करने पर इसे देखता है, यह बता सकता है कि इसमें Person
ऑब्जेक्ट्स शामिल हैं (ताकि आपको इंटेलीजेंस मिल जाए और इसी तरह)।
इसका नकारात्मक पक्ष यह है कि पुराने C # 1.0 और 1.1 कोड (इससे पहले कि वे जेनरिक जोड़ते हैं) इन नए को नहीं समझते हैं List<something>
, इसलिए आपको मैन्युअल रूप से चीजों List
को उनके साथ इंटरोपर्ट करने के लिए सादे पुराने में बदलना होगा। यह एक समस्या का इतना बड़ा नहीं है, क्योंकि C # 2.0 बाइनरी कोड पीछे की ओर संगत नहीं है। ऐसा केवल तभी होगा जब आप कुछ पुराने C # 1.0 / 1.1 कोड को C # 2.0 में अपग्रेड कर रहे हों
ArrayList<Person> foo = new ArrayList<Person>();
सतह पर यह समान दिखता है, और यह सॉर्ट-ऑफ है। कंपाइलर आपको उन चीजों को डालने से भी रोकेगा Person
जो सूची में नहीं हैं ।
फर्क यह है कि पर्दे के पीछे क्या होता है। सी # के विपरीत, जावा नहीं जाता है और एक विशेष निर्माण नहीं करता है ListOfPerson
- यह सिर्फ सादे पुराने का उपयोग करता है ArrayList
जो हमेशा जावा में रहा है। जब आप चीजों को सरणी से बाहर निकालते हैं, तो सामान्य Person p = (Person)foo.get(1);
कास्टिंग-नृत्य अभी भी करना पड़ता है। कंपाइलर आपको की-प्रेस सेव कर रहा है, लेकिन स्पीड हिट / कास्टिंग अभी भी वैसे ही लगी हुई है जैसे कि हमेशा से थी।
जब लोग "टाइप एरासुर" का उल्लेख करते हैं, तो यह वही है जो वे बात कर रहे हैं। संकलक आपके लिए कास्ट्स सम्मिलित करता है, और फिर इस तथ्य को 'मिटा देता है' कि इसका मतलब Person
सिर्फ सूची नहीं हैObject
इस दृष्टिकोण का लाभ यह है कि पुराने कोड जो जेनेरिक को नहीं समझते हैं उन्हें देखभाल करने की आवश्यकता नहीं है। यह अभी भी उसी पुराने के साथ काम कर रहा है ArrayList
जैसा कि हमेशा होता है। जावा की दुनिया में यह अधिक महत्वपूर्ण है क्योंकि वे जावा 5 का उपयोग करके संकलन कोड का समर्थन करना चाहते थे, और इसे पुराने 1.4 या पिछले जेवीएम पर चलाया गया था, जिसे Microsoft ने जानबूझकर परेशान नहीं करने का फैसला किया था।
नकारात्मक पक्ष वह गति है जिसका मैंने पहले उल्लेख किया था, और इसलिए भी कि कोई ListOfPerson
छद्म वर्ग या ऐसा कुछ भी नहीं है जो .class फ़ाइलों में जा रहा हो, कोड जो बाद में इस पर दिखता है (प्रतिबिंब के साथ, या यदि आप इसे किसी अन्य संग्रह से बाहर खींचते हैं। जहां इसे में परिवर्तित किया Object
गया है) किसी भी तरह से यह नहीं बता सकता है कि यह केवल एक सूची है Person
और केवल किसी अन्य सरणी सूची से युक्त नहीं है।
std::list<Person>* foo = new std::list<Person>();
यह सी # और जावा जेनरिक की तरह दिखता है, और यह वही करेगा जो आपको लगता है कि इसे करना चाहिए, लेकिन पर्दे के पीछे अलग-अलग चीजें हो रही हैं।
इसमें C # जेनेरिक के साथ सबसे आम है कि यह विशेष pseudo-classes
प्रकार की सूचनाओं को फेंकने के बजाय विशेष बनाता है जैसे जावा करता है, लेकिन यह मछली की पूरी तरह से अलग केतली है।
C # और Java दोनों आउटपुट देते हैं जो वर्चुअल मशीनों के लिए डिज़ाइन किया गया है। यदि आप कुछ कोड लिखते हैं, जिसमें एक Person
वर्ग है, तो दोनों स्थितियों में एक Person
वर्ग के बारे में कुछ जानकारी .dll या .class फ़ाइल में जाएगी, और JVM / CLR इसके साथ सामान करेगा।
C ++ कच्चे x86 बाइनरी कोड का उत्पादन करता है। सब कुछ एक वस्तु नहीं है , और कोई अंतर्निहित आभासी मशीन नहीं है जिसे किसी Person
वर्ग के बारे में जानना आवश्यक है । कोई बॉक्सिंग या अनबॉक्सिंग नहीं है, और फ़ंक्शंस कक्षाओं, या वास्तव में कुछ भी करने के लिए नहीं है।
इस वजह से, C ++ कंपाइलर आपके द्वारा टेम्प्लेट के साथ क्या कर सकता है, इस पर कोई प्रतिबंध नहीं है - मूल रूप से कोई भी कोड जिसे आप मैन्युअल रूप से लिख सकते हैं, आप अपने लिए लिखने के लिए टेम्प्लेट प्राप्त कर सकते हैं।
सबसे स्पष्ट उदाहरण चीजों को जोड़ रहा है:
C # और Java में, जेनेरिक सिस्टम को यह जानने की जरूरत है कि एक क्लास के लिए कौन से तरीके उपलब्ध हैं, और इसे वर्चुअल मशीन के पास भेजना होगा। इसका यह बताने का एकमात्र तरीका है कि वास्तविक कक्षा को हार्ड-कोडिंग करके या इंटरफेस का उपयोग करके। उदाहरण के लिए:
string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }
यह कोड C # या Java में संकलित नहीं होगा, क्योंकि यह नहीं जानता कि प्रकार T
वास्तव में नाम () नामक एक विधि प्रदान करता है। आपको इसे बताना होगा - C # में इस तरह:
interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }
और फिर आपको यह सुनिश्चित करना होगा कि आप जिन चीजों को जोड़ने के लिए पास हों, वे IHasName इंटरफ़ेस को लागू करें और इसी तरह। जावा सिंटैक्स अलग है ( <T extends IHasName>
), लेकिन यह समान समस्याओं से ग्रस्त है।
इस समस्या के लिए 'क्लासिक' मामला एक फ़ंक्शन लिखने की कोशिश कर रहा है जो ऐसा करता है
string addNames<T>( T first, T second ) { return first + second; }
आप वास्तव में इस कोड को नहीं लिख सकते क्योंकि इसमें +
विधि के साथ एक इंटरफ़ेस घोषित करने के कोई तरीके नहीं हैं । आप विफल हुए।
C ++ इनमें से किसी भी समस्या से ग्रस्त है। कंपाइलर किसी भी वीएम को नीचे टाइपिंग के बारे में परवाह नहीं करता है - यदि आपकी दोनों वस्तुओं में एक .Name () फ़ंक्शन है, तो वह संकलन करेगा। यदि वे नहीं करते हैं, तो यह नहीं होगा। सरल।
इसलिए यह अब आपके पास है :-)
int addNames<T>( T first, T second ) { return first + second; }
C # में नहीं लिख सकते । सामान्य प्रकार को इंटरफ़ेस के बजाय एक वर्ग तक सीमित किया जा सकता है, और इसमें +
ऑपरेटर के साथ एक वर्ग घोषित करने का एक तरीका है।
C ++ शायद ही कभी "जेनरिक" शब्दावली का उपयोग करता है। इसके बजाय, "टेम्पलेट" शब्द का उपयोग किया जाता है और अधिक सटीक होता है। टेम्पलेट एक जेनेरिक डिज़ाइन को प्राप्त करने के लिए एक तकनीक का वर्णन करता है ।
C ++ टेम्प्लेट दो मुख्य कारणों के लिए C # और Java दोनों को लागू करने से बहुत अलग है। पहला कारण यह है कि C ++ टेम्प्लेट न केवल संकलन-समय प्रकार तर्क देते हैं, बल्कि संकलन-समय कांस्ट-वैल्यू तर्क भी देते हैं: टेम्प्लेट पूर्णांक या फ़ंक्शन हस्ताक्षर के रूप में दिए जा सकते हैं। इसका मतलब है कि आप संकलन समय पर कुछ बहुत ही मजेदार चीजें कर सकते हैं, जैसे गणना:
template <unsigned int N>
struct product {
static unsigned int const VALUE = N * product<N - 1>::VALUE;
};
template <>
struct product<1> {
static unsigned int const VALUE = 1;
};
// Usage:
unsigned int const p5 = product<5>::VALUE;
यह कोड C ++ टेम्प्लेट की अन्य विशिष्ट सुविधा का भी उपयोग करता है, अर्थात् टेम्पलेट विशेषज्ञता। कोड एक वर्ग टेम्पलेट को परिभाषित करता है, product
जिसमें एक मान तर्क होता है। यह उस टेम्प्लेट के लिए एक विशेषज्ञता को भी परिभाषित करता है जिसका उपयोग जब भी तर्क का मूल्यांकन किया जाता है 1. यह मुझे टेम्प्लेट प्रतियोगिताओं पर एक पुनरावृत्ति को परिभाषित करने की अनुमति देता है। मेरा मानना है कि यह पहली बार आंद्रेई अलेक्जेंड्रेस्कु द्वारा खोजा गया था ।
C ++ के लिए टेम्पलेट विशेषज्ञता महत्वपूर्ण है क्योंकि यह डेटा संरचनाओं में संरचनात्मक अंतर के लिए अनुमति देता है। संपूर्ण के रूप में टेम्प्लेट एक प्रकार के इंटरफ़ेस को एकीकृत करने का एक साधन है। हालाँकि, हालांकि यह वांछनीय है, सभी प्रकार के कार्यान्वयन के अंदर समान रूप से व्यवहार नहीं किया जा सकता है। C ++ टेम्पलेट इसे ध्यान में रखते हैं। यह वही अंतर है जो OOP वर्चुअल तरीकों के ओवरराइडिंग के साथ इंटरफेस और कार्यान्वयन के बीच बनाता है।
C ++ टेम्प्लेट इसके एल्गोरिथम प्रोग्रामिंग प्रतिमान के लिए आवश्यक हैं। उदाहरण के लिए, कंटेनरों के लिए लगभग सभी एल्गोरिदम को उन कार्यों के रूप में परिभाषित किया गया है जो कंटेनर प्रकार को एक टेम्पलेट प्रकार के रूप में स्वीकार करते हैं और उनके साथ समान व्यवहार करते हैं। वास्तव में, यह बिल्कुल सही नहीं है: C ++ कंटेनरों पर काम नहीं करता है, बल्कि उन श्रेणियों पर काम करता है जो दो पुनरावृत्तियों द्वारा परिभाषित की जाती हैं, कंटेनर की शुरुआत और पीछे की ओर इशारा करती हैं। इस प्रकार, संपूर्ण सामग्री पुनरावृत्तियों द्वारा परिचालित होती है: आरंभ <= तत्व <अंत।
कंटेनरों के बजाय पुनरावृत्तियों का उपयोग करना उपयोगी है क्योंकि यह एक कंटेनर के हिस्सों को पूरे के बजाय संचालित करने की अनुमति देता है।
C ++ की एक अन्य विशिष्ट विशेषता वर्ग टेम्पलेट्स के लिए आंशिक विशेषज्ञता की संभावना है । यह हास्केल और अन्य कार्यात्मक भाषाओं में तर्कों पर मेल खाने वाले पैटर्न से संबंधित है। उदाहरण के लिए, आइए एक वर्ग पर विचार करें जो तत्वों को संग्रहीत करता है:
template <typename T>
class Store { … }; // (1)
यह किसी भी तत्व प्रकार के लिए काम करता है। लेकिन हम कहते हैं कि हम कुछ विशेष चाल को लागू करके पॉइंटर्स को अन्य प्रकारों की तुलना में अधिक कुशलता से स्टोर कर सकते हैं। हम सभी सूचक प्रकारों के लिए आंशिक रूप से विशेषज्ञता द्वारा ऐसा कर सकते हैं :
template <typename T>
class Store<T*> { … }; // (2)
अब, जब भी हम एक प्रकार के लिए एक कंटेनर टेम्पलेट लेते हैं, तो उपयुक्त परिभाषा का उपयोग किया जाता है:
Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.
एंडर्स हेजलबर्ग ने स्वयं यहां " जेनरिक इन सी #, जावा और सी ++ " के अंतरों का वर्णन किया ।
अंतर क्या हैं, इस बारे में पहले से ही बहुत सारे अच्छे उत्तर हैं, इसलिए मुझे थोड़ा अलग दृष्टिकोण देना चाहिए और क्यों जोड़ना चाहिए ।
जैसा कि पहले ही समझाया गया था, मुख्य अंतर प्रकार का क्षरण है , अर्थात यह तथ्य कि जावा कंपाइलर जेनेरिक प्रकारों को मिटा देता है और वे उत्पन्न बायटेकोड में समाप्त नहीं होते हैं। हालांकि, सवाल यह है कि कोई ऐसा क्यों करेगा? इसका कोई मतलब नहीं है! या करता है?
खैर, विकल्प क्या है? यदि आप भाषा में जेनरिक लागू नहीं करते हैं, तो आप उन्हें कहाँ लागू करते हैं? और जवाब है: वर्चुअल मशीन में। जो पीछे की संगतता को तोड़ता है।
दूसरी ओर टाइप इरेज़र, आपको जेनेरिक क्लाइंट को गैर-जेनेरिक लाइब्रेरीज़ के साथ मिलाने की अनुमति देता है। दूसरे शब्दों में: जावा 5 पर संकलित कोड को अभी भी जावा 1.4 में तैनात किया जा सकता है।
हालाँकि, Microsoft ने जेनरिक के लिए पीछे की संगतता को तोड़ने का फैसला किया। यही कारण है कि .NET जेनरिक जावा जेनेरिक की तुलना में "बेहतर" हैं।
बेशक, सूर्य बेवकूफ या कायर नहीं हैं। जब उन्होंने "चिकेन आउट" किया, इसका कारण यह था कि जावा ने जेनेटिक्स को पेश करते समय .NET की तुलना में काफी पुराना और अधिक व्यापक था। (वे दोनों दुनिया में एक ही समय में लगभग पेश किए गए थे।) पीछे की ओर संगतता को तोड़ना एक बहुत बड़ा दर्द होता।
अभी तक एक और तरीका रखें: जावा में, जेनेरिक भाषा का एक हिस्सा है (जिसका अर्थ है कि वे केवल जावा पर लागू होते हैं , अन्य भाषाओं में नहीं), .NET में वे वर्चुअल मशीन (जिसका अर्थ है कि वे सभी पर लागू होते हैं) भाषाओं हैं, नहीं) बस C # और Visual Basic.NET)।
LINQ, लैम्ब्डा एक्सप्रेशंस, लोकल वैरिएबल टाइप इंट्रेंस, अनाम टाइप्स और एक्सप्रेशन ट्री जैसे .NET फीचर्स से इसकी तुलना करें: ये सभी लैंग्वेज फीचर्स हैं। इसीलिए VB.NET और C # के बीच सूक्ष्म अंतर हैं: यदि वे सुविधाएँ VM का हिस्सा थीं, तो वे सभी भाषाओं में समान होंगी । लेकिन CLR नहीं बदला है: यह अभी भी .NET 3.5 SP1 में वैसा ही है जैसा कि .NET 2.0 में था। आप .NET 3.5 कंपाइलर के साथ LINQ का उपयोग करने वाले C # प्रोग्राम को संकलित कर सकते हैं और अभी भी इसे .NET 2.0 पर चला सकते हैं, बशर्ते कि आप किसी भी .NET 3.5 लाइब्रेरी का उपयोग न करें। यह जेनेरिक और .NET 1.1 के साथ काम नहीं करेगा , लेकिन यह जावा और जावा 1.4 के साथ काम करेगा ।
ArrayList<T>
एक (छिपे हुए) स्थिर Class<T>
क्षेत्र के साथ आंतरिक रूप से नामित नए प्रकार के रूप में उत्सर्जित किया जा सकता है । जब तक जेनेरिक लिबास के नए संस्करण को 1.5+ बाइट कोड के साथ तैनात किया गया है, तब तक यह 1.4- JVM पर चलने में सक्षम होगा।
मेरे पिछले पोस्ट करने के लिए अनुवर्ती।
टेम्प्लेट मुख्य कारणों में से एक है कि C ++ इंटेलीजेंस में इतना कम क्यों विफल हो जाता है, भले ही आईडीई का उपयोग नहीं किया गया हो। टेम्पलेट विशेषज्ञता के कारण, IDE वास्तव में निश्चित नहीं हो सकता है कि कोई सदस्य मौजूद है या नहीं। विचार करें:
template <typename T>
struct X {
void foo() { }
};
template <>
struct X<int> { };
typedef int my_int_type;
X<my_int_type> a;
a.|
अब, कर्सर संकेतित स्थिति में है और यह आईडीई के लिए उस बिंदु पर कहना मुश्किल है, अगर और क्या है, सदस्यों के a
पास। अन्य भाषाओं के लिए पार्सिंग सीधी होगी लेकिन C ++ के लिए, पहले से काफी मूल्यांकन की आवश्यकता है।
ये और ख़राब हो जाता है। क्या होगा अगर my_int_type
एक वर्ग टेम्पलेट के अंदर भी परिभाषित किया गया? अब इसका प्रकार दूसरे प्रकार के तर्क पर निर्भर करेगा। और यहाँ, यहां तक कि संकलक भी विफल हो जाते हैं।
template <typename T>
struct Y {
typedef T my_type;
};
X<Y<int>::my_type> b;
थोड़ा सोचने के बाद, एक प्रोग्रामर यह निष्कर्ष निकालता है कि यह कोड उपरोक्त के समान है: Y<int>::my_type
करने के लिए हल करता है int
, इसलिए b
उसी प्रकार होना चाहिए a
, है ना?
गलत। जिस बिंदु पर संकलक इस कथन को हल करने की कोशिश करता है, वह वास्तव में Y<int>::my_type
अभी तक नहीं जानता है! इसलिए, यह नहीं जानता कि यह एक प्रकार है। यह कुछ और हो सकता है, उदाहरण के लिए एक सदस्य फ़ंक्शन या फ़ील्ड। यह अस्पष्टता को जन्म दे सकता है (हालांकि वर्तमान मामले में नहीं), इसलिए कंपाइलर विफल हो जाता है। हमें यह स्पष्ट रूप से बताना होगा कि हम एक प्रकार के नाम का उल्लेख करते हैं:
X<typename Y<int>::my_type> b;
अब, कोड संकलित करता है। इस स्थिति से कैसे अस्पष्टता उत्पन्न होती है, यह देखने के लिए निम्नलिखित कोड पर विचार करें:
Y<int>::my_type(123);
यह कोड स्टेटमेंट पूरी तरह से मान्य है और फ़ंक्शन कॉल को निष्पादित करने के लिए C ++ को बताता है Y<int>::my_type
। हालांकि, यदि my_type
कोई फ़ंक्शन नहीं है, बल्कि एक प्रकार है, तो यह कथन अभी भी मान्य होगा और एक विशेष कलाकारों (फ़ंक्शन-स्टाइल कास्ट) का प्रदर्शन करेगा जो अक्सर एक रचनाकार मंगलाचरण होता है। कंपाइलर यह नहीं बता सकता है कि हमारा क्या मतलब है इसलिए हमें यहां से निकलना होगा।
जावा और C # दोनों ने अपनी पहली भाषा रिलीज़ के बाद जेनरिक की शुरुआत की। हालांकि, इस बात पर मतभेद हैं कि जब जेनरिक को पेश किया गया तो कोर लाइब्रेरी कैसे बदल गई। सी # की जेनरिक सिर्फ कंपाइलर मैजिक नहीं है और इसलिए मौजूदा लाइब्रेरी क्लासेस को पीछे की अनुकूलता को तोड़े बिना इसे बनाना संभव नहीं था ।
उदाहरण के लिए, जावा में मौजूदा कलेक्शंस फ्रेमवर्क पूरी तरह से जेनेरिक था । जावा के पास संग्रह कक्षाओं का एक सामान्य और विरासत गैर-सामान्य संस्करण दोनों नहीं है। कुछ मायनों में यह बहुत साफ है - अगर आपको C # में एक संग्रह का उपयोग करने की आवश्यकता है, तो गैर-सामान्य संस्करण के साथ जाने के लिए वास्तव में बहुत कम कारण है, लेकिन उन विरासत वर्गों को जगह मिलती है, जो परिदृश्य को अव्यवस्थित करते हैं।
एक और उल्लेखनीय अंतर जावा और C # में Enum कक्षाएं हैं। जावा के एनम की यह कुछ हद तक यातनापूर्ण परिभाषा है:
// java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
(देखें एंजेलिका लैंगर की स्पष्ट व्याख्या कि ऐसा क्यों है। अनिवार्य रूप से, इसका अर्थ है कि जावा स्ट्रिंग से अपने Enum मान तक सुरक्षित पहुँच दे सकता है:
// Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");
इसकी तुलना C # के संस्करण से करें:
// Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");
चूंकि Enum भाषा में पेश किए जाने से पहले C # में पहले से मौजूद था, मौजूदा कोड को तोड़े बिना परिभाषा नहीं बदल सकती थी। इसलिए, संग्रह की तरह, यह इस विरासत की स्थिति में मुख्य पुस्तकालयों में बना हुआ है।
ArrayList
के लिए List<T>
और यह एक नया नाम स्थान में डाल दिया। तथ्य यह है, अगर स्रोत कोड में एक वर्ग दिखाई देता है क्योंकि ArrayList<T>
यह आईएल कोड में एक अलग संकलित उत्पन्न वर्ग नाम बन जाएगा, इसलिए कोई नाम संघर्ष नहीं हो सकता है।
11 महीने देर से, लेकिन मुझे लगता है कि यह सवाल कुछ जावा वाइल्डकार्ड सामान के लिए तैयार है।
यह जावा की एक वाक्यात्मक विशेषता है। मान लीजिए कि आपके पास एक तरीका है:
public <T> void Foo(Collection<T> thing)
और मान लें कि आपको विधि बॉडी में टाइप T को संदर्भित करने की आवश्यकता नहीं है। आप एक नाम T घोषित कर रहे हैं और उसके बाद केवल एक बार इसका उपयोग कर रहे हैं, इसलिए आपको इसके लिए एक नाम क्यों सोचना चाहिए? इसके बजाय, आप लिख सकते हैं:
public void Foo(Collection<?> thing)
प्रश्न-चिह्न संकलक को यह दिखावा करने के लिए कहता है कि आपने एक सामान्य नामित प्रकार का पैरामीटर घोषित किया है जिसे केवल उस स्थान पर एक बार दिखाई देने की आवश्यकता है।
वाइल्डकार्ड्स के साथ ऐसा कुछ भी नहीं है जिसे आप एक नामित प्रकार के पैरामीटर के साथ भी नहीं कर सकते (जो कि ये चीजें हमेशा C ++ और C # में की जाती हैं)।
class Foo<T extends List<?>>
और उपयोग करें Foo<StringList>
लेकिन C # में आपको उस अतिरिक्त प्रकार के पैरामीटर को जोड़ना होगा: class Foo<T, T2> where T : IList<T2>
और क्लंकी का उपयोग करें Foo<StringList, String>
।
विकिपीडिया में जावा / सी # जेनरिक और जावा जेनेरिक / सी ++ टेम्प्लेट दोनों की तुलना में शानदार लेखन है । जेनेरिक्स पर मुख्य लेख थोड़ा भरा हुआ लगता है, लेकिन यह उस में कुछ अच्छी जानकारी है।
सबसे बड़ी शिकायत टाइप इरेज़र है। उस में, क्रम के समय जेनरिक लागू नहीं किया जाता है। यहाँ इस विषय पर कुछ सन डॉक्स का लिंक दिया गया है ।
जेनरेशन टाइप इरेज़र द्वारा कार्यान्वित किए जाते हैं: सामान्य प्रकार की जानकारी केवल संकलन समय पर मौजूद होती है, जिसके बाद इसे कंपाइलर द्वारा मिटा दिया जाता है।
C ++ टेम्प्लेट वास्तव में उनके C # और Java समकक्षों की तुलना में बहुत अधिक शक्तिशाली होते हैं क्योंकि उनका मूल्यांकन संकलन समय और समर्थन विशेषज्ञता पर किया जाता है। यह टेम्प्लेट मेटा-प्रोग्रामिंग के लिए अनुमति देता है और C ++ कंपाइलर को ट्यूरिंग मशीन के बराबर बनाता है (यानी संकलन प्रक्रिया के दौरान आप किसी भी चीज की गणना कर सकते हैं जो ट्यूरिंग मशीन के साथ अभिकलन है)।
ऐसा लगता है, अन्य बहुत ही दिलचस्प प्रस्तावों के बीच, जेनेरिक को परिष्कृत करने और पीछे की संगतता को तोड़ने के बारे में एक है:
वर्तमान में, जेनेरिक को इरेज़र का उपयोग करके कार्यान्वित किया जाता है, जिसका अर्थ है कि सामान्य प्रकार की जानकारी रनटाइम पर उपलब्ध नहीं है, जो लिखने के लिए किसी प्रकार का कोड कठिन बनाता है। पुरानी गैर-सामान्य कोड के साथ पश्चगामी संगतता का समर्थन करने के लिए जेनरिक को इस तरह से लागू किया गया था। संशोधित जेनरिक रनटाइम में जेनेरिक प्रकार की जानकारी उपलब्ध कराएगा, जो विरासत को गैर-जेनेरिक कोड को तोड़ देगा। हालांकि, नील गैटर ने केवल निर्दिष्ट किए जाने पर ही प्रकार को पुन: प्रयोज्य बनाने का प्रस्ताव दिया है, ताकि पिछड़े अनुकूलता को न तोड़े।
NB: मेरे पास टिप्पणी करने के लिए पर्याप्त बिंदु नहीं है, इसलिए इसे उचित उत्तर के लिए टिप्पणी के रूप में स्थानांतरित करने के लिए स्वतंत्र महसूस करें।
लोकप्रिय विश्वास के विपरीत, जिसे मैं कभी नहीं समझता कि यह कहां से आया है, .net ने पिछड़ी संगतता को तोड़ने के बिना सच्चे जेनरिक को लागू किया, और उन्होंने इसके लिए स्पष्ट प्रयास किया। आपको अपने गैर-जेनेरिक .net 1.0 कोड को जेनेटिक्स में बदलने की जरूरत नहीं है। नेट 2.0 में इस्तेमाल किया जा सकता है। जेनेरिक और गैर-जेनेरिक दोनों सूचियाँ अभी भी .net ढाँचे 2.0 में 4.0 तक उपलब्ध हैं, वास्तव में कुछ और नहीं बल्कि पिछड़ी अनुकूलता के कारण। इसलिए पुराने कोड जो अभी भी गैर-जेनेरिक ArrayList का उपयोग करते थे, अब भी काम करेंगे, और पहले की तरह ही ArrayList वर्ग का उपयोग करेंगे। पिछड़े कोड की संगतता हमेशा 1.0 से अब तक बनी हुई है ... इसलिए यहां तक कि .net 4.0 में, आपको अभी भी 1.0 बीसीएल से किसी भी गैर-जेनेरिक क्लास का उपयोग करने का विकल्प चुनना है यदि आप ऐसा करना चाहते हैं।
इसलिए मुझे नहीं लगता कि जावा को सच्ची जेनरिक का समर्थन करने के लिए पिछड़ेपन को तोड़ना होगा।
ArrayList<Foo>
कि यह एक पुरानी विधि को पारित करना चाहता है जिसे ArrayList
उदाहरणों के साथ आबाद करना है Foo
। यदि कोई ArrayList<foo>
नहीं है ArrayList
, तो वह काम कैसे करता है?