जेनेरिक के बारे में क्या अच्छा है, उनका उपयोग क्यों करें?


87

मैंने सोचा था कि मैं इस सॉफ्टबॉल की पेशकश करूँगा जिसे पार्क के बाहर मारना चाहूंगा। जेनरिक क्या हैं, जेनेरिक के क्या फायदे हैं, क्यों, कहाँ, मुझे उनका उपयोग कैसे करना चाहिए? कृपया इसे काफी बुनियादी रखें। धन्यवाद।


1
का डुप्लीकेट stackoverflow.com/questions/77632/... । लेकिन सरल उत्तर, भले ही संग्रह आपके एपीआई का हिस्सा नहीं हैं, मुझे आंतरिक कार्यान्वयन में भी अनावश्यक कास्टिंग पसंद नहीं है।
मैथ्यू फ्लैशेन

सवाल काफी हद तक समान है, लेकिन मुझे नहीं लगता कि यह स्वीकार किए गए उत्तर का जवाब है।
टॉम हॉकिन -

1
इसके अलावा checkout stackoverflow.com/questions/520527
क्लिंट मिलर

4
@MatthewFlaschen अजीब, इस प्रश्न के लिए आपका डुप्लिकेट निर्देश ... मैट्रिक्स में गड़बड़!
जौलम

@jcollum, मुझे लगता है कि मैंने जो मूल प्रश्न पोस्ट किया था, उस पर इस सवाल को मिला दिया गया था।
मैथ्यू फ्लेशेन

जवाबों:


126
  • आपको कोड लिखने / लाइब्रेरी विधियों का उपयोग करने की अनुमति देता है जो प्रकार-सुरक्षित हैं, अर्थात सूची <string> स्ट्रिंग की सूची होने की गारंटी है।
  • जेनरिक के परिणामस्वरूप कंपाइलर टाइप सुरक्षा के लिए कोड पर संकलन-समय की जांच कर सकता है, यानी आप स्ट्रिंग्स की सूची में एक इंट लगाने की कोशिश कर रहे हैं? ArrayList का उपयोग करने से कम पारदर्शी रनटाइम त्रुटि होगी।
  • वस्तुओं का उपयोग करने की तुलना में तेजी से यह या तो बॉक्सिंग / अनबॉक्सिंग से बचता है (जहां .net को मूल्य प्रकारों को संदर्भ प्रकारों या इसके विपरीत में बदलना पड़ता है ) या वस्तुओं से आवश्यक संदर्भ प्रकार में कास्टिंग करना।
  • आपको कोड लिखने की अनुमति देता है जो एक ही अंतर्निहित व्यवहार के साथ कई प्रकारों पर लागू होता है, अर्थात एक शब्दकोश <string, int> एक शब्दकोश के रूप में एक ही अंतर्निहित कोड का उपयोग करता है <DateTime, double>; जेनरिक का उपयोग करते हुए, फ्रेमवर्क टीम को केवल उपरोक्त लाभों के साथ दोनों परिणामों को प्राप्त करने के लिए कोड का एक टुकड़ा लिखना था।

9
आह, बहुत अच्छा है, और मुझे बुलेट सूची पसंद है। मुझे लगता है कि यह अब तक का सबसे पूर्ण रूप से संक्षिप्त जवाब है।
MrBoJangles

संपूर्ण विवरण "संग्रह" संदर्भ में है। मैं व्यापक रूप की तलाश में हूं। कोई विचार?
जंगल_मोल

अच्छा जवाब मैं सिर्फ 2 अन्य फायदे जोड़ना चाहता हूं, जेनेरिक प्रतिबंध के पास 2 फायदे हैं 1- आप सामान्य प्रकार में विवश प्रकार के गुणों का उपयोग कर सकते हैं (उदाहरण के लिए जहां T: IComparable, ComparTo का उपयोग करने का प्रावधान करता है) 2- वह व्यक्ति जो लिखेगा कोड के बाद आपको पता चलेगा कि क्या होगा
Hamit YILDIRIM

52

मुझे वास्तव में खुद को दोहराने से नफरत है। मुझे एक ही चीज टाइप करने से ज्यादा नफरत है जितना मुझे करना है। मैं मामूली अंतर के साथ कई बार चीजों को आराम करना पसंद नहीं करता।

बनाने के बजाय:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

मैं एक पुन: प्रयोज्य वर्ग का निर्माण कर सकता हूं ... (उस मामले में जहां आप किसी कारण से कच्चे संग्रह का उपयोग नहीं करना चाहते हैं)

class MyList<T> {
   T get(int index) { ... }
}

अब मैं 3x अधिक कुशल हूं और मुझे केवल एक प्रति को बनाए रखना है। आप कम कोड क्यों बनाए रखना चाहते हैं?

यह गैर-संग्रह कक्षाओं जैसे कि एक Callable<T>या Reference<T>अन्य वर्गों के साथ बातचीत करने के लिए भी सही है । क्या तुम सच में विस्तार करने के लिए करना चाहते हैं Callable<T>और Future<T>और हर अन्य संबद्ध वर्ग प्रकार सुरक्षित संस्करण बनाने के लिए?

मैं नही।


1
मुझे यकीन नहीं कि मैं समझा हूँ। मैं आपको "MyObject" रैपर बनाने का सुझाव नहीं दे रहा हूं, यह भयानक होगा। मैं सुझाव दे रहा हूं कि यदि आपकी वस्तुओं का संग्रह कारों का संग्रह है, तो आप एक गैरेज ऑब्जेक्ट लिखते हैं। इसे (कार) नहीं मिलेगा, इसके पास खरीदने के तरीके (100) होंगे जो $ 100 ईंधन खरीदेंगे और इसे उन कारों के बीच वितरित करेंगे जिनकी इसे सबसे ज्यादा जरूरत है। मैं वास्तविक व्यावसायिक वर्गों के बारे में बात कर रहा हूं, न कि केवल "रैपर"। उदाहरण के लिए, आपको संग्रह के बाहर लगभग कभी भी (कार) या लूप नहीं मिलना चाहिए - आप इसके सदस्यों के लिए ऑब्जेक्ट नहीं मांगते हैं, इसके बजाय आप इसे आपके लिए एक ऑपरेशन करने के लिए कहते हैं।
बिल K

अपने गैरेज के भीतर भी, आपको टाइप-सेफ्टी चाहिए। तो या तो आपके पास List <Cars>, ListOfCars हैं या आप हर जगह कास्ट करते हैं। मुझे लगता है कि आवश्यक न्यूनतम समय से अधिक प्रकार को बताने की आवश्यकता नहीं है।
जेम्स स्कैच

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

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

उदाहरण अवधारणा को अधिक व्यावहारिक बनाता है। उसके लिए धन्यवाद।
रेअवेल्वेस

22

टाइपकास्ट करने की आवश्यकता नहीं है जावा जेनेरिक का सबसे बड़ा लाभ है , क्योंकि यह संकलन-समय पर प्रकार की जाँच करेगा। यह ClassCastExceptions की संभावना को कम करेगा जिसे रनटाइम पर फेंका जा सकता है, और अधिक मजबूत कोड हो सकता है।

लेकिन मुझे संदेह है कि आप इससे पूरी तरह परिचित हैं।

हर बार जब मैं जेनरिक को देखता हूं तो यह मुझे सिरदर्द देता है। मुझे लगता है कि जावा का सबसे अच्छा हिस्सा यह सादगी और न्यूनतम सिंटैक्स है और जेनेरिक सरल नहीं हैं और नए सिंटैक्स की एक महत्वपूर्ण राशि जोड़ते हैं।

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

मॉडर्न IDE जेनेरिक से राइटिंग कोड को आसान बनाते हैं।

अधिकांश आधुनिक, सभ्य IDE सामान्य रूप से कोड पूरा करने के साथ, जेनरिक के साथ कोड लिखने में सहायता करने के लिए पर्याप्त स्मार्ट हैं।

यहाँ एक के Map<String, Integer>साथ बनाने का एक उदाहरण है HashMap। कोड मुझे टाइप करना होगा:

Map<String, Integer> m = new HashMap<String, Integer>();

और वास्तव में, यह सिर्फ एक नया बनाने के लिए टाइप करने के लिए बहुत कुछ है HashMap। हालांकि, वास्तव में, मुझे केवल इतना ही लिखना था कि ग्रहण से पहले मुझे पता था कि मुझे क्या चाहिए:

Map<String, Integer> m = new Ha Ctrl+Space

सच है, मुझे HashMapउम्मीदवारों की एक सूची से चयन करने की आवश्यकता थी , लेकिन मूल रूप से आईडीई को पता था कि क्या जोड़ना है, जिसमें सामान्य प्रकार शामिल हैं। सही उपकरण के साथ, जेनेरिक का उपयोग करना बहुत बुरा नहीं है।

इसके अलावा, चूंकि प्रकार ज्ञात हैं, जेनेरिक संग्रह से तत्वों को पुनः प्राप्त करते समय, IDE कार्य करेगा जैसे कि वह वस्तु पहले से ही अपने घोषित प्रकार की वस्तु है - यह जानने के लिए IDE के लिए कास्टिंग करने की कोई आवश्यकता नहीं है कि वस्तु का प्रकार क्या है है।

जेनरिक का एक प्रमुख लाभ यह है कि यह नए जावा 5 सुविधाओं के साथ अच्छा खेलता है। यहाँ एक पूर्णांक को वापस लेने Setऔर उसके कुल की गणना करने का एक उदाहरण है :

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

उस कोड ऑफ कोड में, तीन नए जावा 5 फीचर मौजूद हैं:

सबसे पहले, प्राइमेटिक्स की जेनरिक और ऑटोबॉक्सिंग निम्नलिखित लाइनों की अनुमति देती हैं:

set.add(10);
set.add(42);

पूर्णांक 10को Integerमान के साथ ऑटोबॉक्स किया गया है 10। (और उसी के लिए 42) फिर उस Integerमें टॉस किया Setजाता है, जिसे Integers धारण करना ज्ञात है में फेंकने की कोशिश करने से Stringसंकलन में त्रुटि होगी।

अगला, प्रत्येक लूप के लिए उन तीनों को लेता है:

for (int i : set) {
  total += i;
}

सबसे पहले, Setयुक्त Integers का उपयोग प्रत्येक लूप में किया जाता है। प्रत्येक तत्व को एक घोषित किया जाता है intऔर इसे अनुमति दी जाती है क्योंकि Integerइसे वापस आदिम में अनबॉक्स किया जाता है int। और यह तथ्य यह है कि यह अनबॉक्सिंग ज्ञात है क्योंकि जेनेरिक का उपयोग यह निर्दिष्ट करने के लिए किया गया था कि इसमें Integerआयोजित किए गए थे Set

जेनरिक वह गोंद हो सकता है जो जावा 5 में शुरू की गई नई विशेषताओं को एक साथ लाता है, और यह सिर्फ कोडिंग को सरल और सुरक्षित बनाता है। और ज्यादातर समय IDE अच्छे सुझावों के साथ आपकी मदद करने के लिए पर्याप्त स्मार्ट होते हैं, इसलिए आमतौर पर, यह बहुत अधिक टाइपिंग नहीं करेगा।

और स्पष्ट रूप से, जैसा कि Setउदाहरण से देखा जा सकता है , मुझे लगता है कि जावा 5 सुविधाओं का उपयोग कोड को अधिक संक्षिप्त और मजबूत बना सकता है।

संपादित करें - जेनरिक के बिना एक उदाहरण

निम्नलिखित Setजेनेरिक के उपयोग के बिना उपरोक्त उदाहरण का एक चित्रण है । यह संभव है, लेकिन बिल्कुल सुखद नहीं है:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(नोट: उपरोक्त कोड संकलन-समय पर अनियंत्रित रूपांतरण चेतावनी उत्पन्न करेगा।)

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

set.add(10);
set.add(42);

उपरोक्त पंक्तियों में, ऑटोबॉक्सिंग खेल में है - आदिम intमूल्य 10और वस्तुओं 42में ऑटोबॉक्सिंग किया जा रहा है Integer, जिन्हें इसमें जोड़ा जा रहा है Set। हालांकि, ध्यान रखें, Integerऑब्जेक्ट्स को Objectएस के रूप में संभाला जा रहा है , क्योंकि कंपाइलर को यह जानने में मदद करने के लिए कोई प्रकार की जानकारी नहीं है कि किस प्रकार की Setअपेक्षा की जानी चाहिए।

for (Object o : set) {

यह वह हिस्सा है जो महत्वपूर्ण है। प्रत्येक लूप के काम करने का कारण यह Setहै कि Iterableइंटरफ़ेस लागू होता है, जो Iteratorयदि मौजूद है, तो एक प्रकार की जानकारी के साथ लौटता है । ( Iterator<T>, यह है)

हालाँकि, चूंकि कोई प्रकार की जानकारी नहीं है, इसलिए Setवह लौटाएगा Iteratorजो मानों Setको Objectएस के रूप में लौटाएगा , और इसीलिए प्रत्येक लूप में प्राप्त किया जा रहा तत्व प्रकार का होना चाहिए Object

अब जब Objectसे पुनर्प्राप्त किया गया है Set, तो Integerइसके अलावा प्रदर्शन करने के लिए इसे मैन्युअल रूप से डाला जाना चाहिए :

  total += (Integer)o;

यहां, ए से टाइपकास्ट किया Objectजाता है Integer। इस मामले में, हम जानते हैं कि यह हमेशा काम करेगा, लेकिन मैनुअल टाइपकास्टिंग हमेशा मुझे लगता है कि यह एक नाजुक कोड है, जो एक छोटे से बदलाव के कारण क्षतिग्रस्त हो सकता है, जहां। (मुझे लगता है कि हर टाइपकास्ट ClassCastExceptionहोने की प्रतीक्षा है, लेकिन मैं पचाता हूं ...)

Integerअब एक में unboxed है intऔर में इसके प्रदर्शन करने के लिए अनुमति दी intचर total

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


लूप के लिए बढ़ाया के साथ एकीकरण वास्तव में अच्छा है (हालांकि मुझे लगता है कि लानत लूप टूट गया है क्योंकि मैं गैर-जेनेरिक संग्रह के साथ सटीक एक ही वाक्यविन्यास का उपयोग नहीं कर सकता हूं - इसे सिर्फ इंट करने के लिए कास्ट / ऑटोबॉक्स का उपयोग करना चाहिए, यह मिल गया है सभी आवश्यक जानकारी!), लेकिन आप सही हैं, आईडीई में मदद शायद अच्छी है। मुझे अभी भी नहीं पता है कि क्या यह अतिरिक्त सिंटैक्स को सही ठहराने के लिए पर्याप्त है, लेकिन कम से कम ऐसा कुछ है जिससे मैं लाभ उठा सकता हूं (जो मेरे प्रश्न का उत्तर देता है)।
बिल के

@ बिल के: मैंने जेनेरिक का उपयोग किए बिना जावा 5 सुविधाओं का उपयोग करने का एक उदाहरण जोड़ा है।
Coobird

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

15

आप जावा बग डेटाबेस खोज करने के लिए बस से पहले 1.5 जारी किया गया था थे, तो आप के साथ सात गुना अधिक कीड़े पाते हैं NullPointerExceptionकी तुलना में ClassCastException। इसलिए ऐसा नहीं लगता है कि बग को खोजने के लिए, या कम से कम ऐसे कीड़े हैं जो थोड़े से धुएं के परीक्षण के बाद बने रहते हैं।

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

किसी वस्तु के संग्रह को अपने आप में रखना एक बुरी शैली नहीं है (लेकिन तब आम शैली प्रभावी रूप से इनकैप्सुलेशन को अनदेखा करना है)। यह बल्कि इस बात पर निर्भर करता है कि आप क्या कर रहे हैं। "एल्गोरिदम" के लिए संग्रह पास करना जेनरिक के साथ (संकलन के समय या पहले) जांचना थोड़ा आसान है।


6
न केवल यह प्रलेखित है (जो अपने आप में एक बड़ी जीत है), इसे कंपाइलर द्वारा लागू किया गया है।
जेम्स स्कैच

अच्छी बात है, लेकिन यह भी एक वर्ग में संग्रह को घेरने से पूरा नहीं होता है जो "इस प्रकार की वस्तु का संग्रह" को परिभाषित करता है?
बिल के

1
जेम्स: हाँ, यह कोड में प्रलेखित होने के बारे में बात है ।
टॉम ह्विटिन -

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

इसके अलावा, ऑब्जेक्ट का ठीक-ठीक वर्णन करने का कोई तरीका नहीं है कि संग्रह का उपयोग कैसे किया जाता है और इसे सीमित कोड के MUCH बेहतर रूप का उपयोग किया जाता है? मुझे सामान्य कोड में दी गई जानकारी बहुत अच्छे कोड में लगती है।
बिल के

11

जावा में जनक पारमार्थिक बहुरूपता की सुविधा देते हैं । प्रकार के मापदंडों के अनुसार, आप प्रकारों के लिए तर्क पास कर सकते हैं। जिस तरह एक String foo(String s)मॉडल कुछ व्यवहार की तरह है, न केवल किसी विशेष स्ट्रिंग के लिए, बल्कि किसी भी स्ट्रिंग के लिए s, इसलिए एक प्रकार जैसे List<T>मॉडल कुछ व्यवहार, न केवल एक विशिष्ट प्रकार के लिए, बल्कि किसी भी प्रकार के लिएList<T>का कहना है कि किसी भी प्रकार के लिए T, वहाँ का एक प्रकार है Listजिसका तत्व हैं Tरों । तो Listवास्तव में एक प्रकार का निर्माता है । यह एक तर्क के रूप में एक प्रकार लेता है और परिणामस्वरूप एक अन्य प्रकार का निर्माण करता है।

यहां कुछ सामान्य प्रकार के उदाहरण दिए गए हैं जिनका मैं हर दिन उपयोग करता हूं। सबसे पहले, एक बहुत ही उपयोगी सामान्य इंटरफ़ेस:

public interface F<A, B> {
  public B f(A a);
}

यह इंटरफ़ेस कहता है कि कुछ दो प्रकारों के लिए, Aऔर B, एक फ़ंक्शन (जिसे कहा जाता है f) एक लेता है Aऔर एक रिटर्न देता है Bजब आप इस इंटरफ़ेस को लागू करते हैं, Aऔर Bकिसी भी प्रकार का आप चाहते हैं, जब तक आप एक फ़ंक्शन प्रदान करते हैं fजो पूर्व लेता है और बाद में वापस करता है। यहाँ इंटरफ़ेस का एक उदाहरण कार्यान्वयन है:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

जेनरिक से पहले, बहुरूपता के द्वारा प्राप्त किया गया था उपवर्गीकरण का उपयोग कर extendsकीवर्ड। जेनरिक के साथ, हम वास्तव में उपवर्ग के साथ दूर कर सकते हैं और इसके बजाय पैरामीट्रिक बहुरूपता का उपयोग कर सकते हैं। उदाहरण के लिए, किसी भी प्रकार के हैश कोड की गणना के लिए उपयोग किए जाने वाले एक पैरामीटर (जेनेरिक) वर्ग पर विचार करें। Object.hashCode () को ओवरराइड करने के बजाय, हम इस तरह एक जेनेरिक क्लास का उपयोग करेंगे:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

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

जावा के जेनेरिक हालांकि सही नहीं हैं। आप प्रकारों पर अमूर्त कर सकते हैं, लेकिन आप उदाहरण के लिए, प्रकार के रचनाकारों पर अमूर्त नहीं कर सकते। यही है, आप "किसी भी प्रकार के टी" के लिए कह सकते हैं, लेकिन आप "किसी भी प्रकार के टी के लिए नहीं कह सकते हैं जो एक प्रकार का पैरामीटर ए" लेता है।

मैंने जावा जेनरिक की इन सीमाओं के बारे में एक लेख लिखा था, यहाँ।

जेनरिक के साथ एक बड़ी जीत यह है कि वे आपको उपवर्ग से बचने देते हैं। सबक्लासिंग में भंगुर वर्ग पदानुक्रमों का परिणाम होता है जो विस्तार के लिए अजीब होते हैं, और ऐसी कक्षाएं जिन्हें पूरे पदानुक्रम को देखे बिना व्यक्तिगत रूप से समझना मुश्किल है।

जेनरिक से पहले Wereas आप की तरह कक्षाएं हो सकता है Widgetके लिए बढ़ा दी FooWidget, BarWidgetऔर BazWidget, जेनरिक के साथ आप एक ही सामान्य वर्ग हो सकता है Widget<A>कि एक लेता है Foo, Barया Bazइसके निर्माता आप देने के लिए Widget<Foo>, Widget<Bar>और Widget<Baz>


यदि जावा में इंटरफेस नहीं है तो यह उपवर्ग के बारे में एक अच्छा बिंदु होगा। क्या FooWidget, BarWidtet और BazWidget सभी विजेट को लागू करना सही नहीं होगा? मैं इस बारे में गलत हो सकता है .. लेकिन अगर मैं गलत हूं तो यह बहुत अच्छा बिंदु है।
बिल K

हैश मूल्यों की गणना के लिए आपको एक गतिशील सामान्य वर्ग की आवश्यकता क्यों है? क्या नौकरी के लिए उपयुक्त मापदंडों के साथ एक स्थिर कार्य अधिक कुशलता से नहीं होगा?
Ant_222

8

जेनरिक बॉक्सिंग और अनबॉक्सिंग के प्रदर्शन हिट से बचते हैं। मूल रूप से, ArrayList vs List <T> को देखें। दोनों एक ही मूल बातें करते हैं, लेकिन सूची <T> बहुत तेज़ होगी क्योंकि आपको ऑब्जेक्ट से बॉक्स / टू नहीं करना है।


यही इसका सार है, है ना? अच्छा लगा।
MrBoJangles

अच्छी तरह से पूरी तरह से नहीं; वे प्रकार सुरक्षा के लिए उपयोगी हैं + संदर्भ प्रकारों के लिए जातियों को बॉक्सिंग / अनबॉक्सिंग के बिना भी इससे बचने के लिए।
22

इसलिए मुझे लगता है कि इस प्रकार का प्रश्न है, "मैं कभी भी एक ArrayList का उपयोग क्यों करना चाहूंगा?" और, एक उत्तर को खतरे में डालते हुए, मैं कहूंगा कि ArrayLists तब तक ठीक हैं जब तक आप बॉक्स की अपेक्षा नहीं करते हैं और उनके मूल्यों को अनबॉक्स नहीं करते हैं। टिप्पणियाँ?
MrBoJangles 22

नहीं, वे .net 2 के रूप में दोषपूर्ण हैं; केवल पश्चगामी संगतता के लिए उपयोगी है। ArrayLists धीमे हैं, सुरक्षित नहीं हैं और बॉक्सिंग / अनबॉक्सिंग या कास्टिंग की आवश्यकता है।
22

यदि आप मान प्रकार (जैसे ints या संरचना) संग्रहीत नहीं कर रहे हैं, तो ArrayList का उपयोग करते समय कोई बॉक्सिंग नहीं है - कक्षाएं पहले से ही 'ऑब्जेक्ट' हैं। सूची <T> का उपयोग करना सुरक्षा के प्रकार के कारण अभी भी बहुत बेहतर है, और यदि आप वास्तव में वस्तुओं को संग्रहीत करना चाहते हैं, तो सूची <ऑब्जेक्ट> का उपयोग करना बेहतर है।
विल्का

6

Generics के लिए सबसे अच्छा लाभ कोड का पुन: उपयोग है। आओ हम कहते हैं कि आपके पास बहुत सी व्यावसायिक वस्तुएं हैं, और आप प्रत्येक इकाई के लिए समान कार्य करने के लिए बहुत समान कोड लिखने जा रहे हैं। (IE लाइनक्यू टू SQL ऑपरेशंस)।

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

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

ऊपर दिए गए DoSomething विधि में विभिन्न प्रकारों को दिए गए एक ही सेवा के पुन: उपयोग पर ध्यान दें। सचमुच सुरुचिपूर्ण!

आपके काम के लिए जेनरिक का उपयोग करने के कई अन्य महान कारण हैं, यह मेरा पसंदीदा है।


5

मैं उन्हें पसंद करता हूं क्योंकि वे आपको एक कस्टम प्रकार को परिभाषित करने का एक त्वरित तरीका देते हैं (जैसा कि मैं वैसे भी उनका उपयोग करता हूं)।

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

Dictionary<int, string> dictionary = new Dictionary<int, string>();

और कम्पाइलर / आईडीई बाकी भारी उठाने का काम करता है। एक शब्दकोश विशेष रूप से आपको कुंजी के रूप में पहले प्रकार का उपयोग करने देता है (कोई दोहराए गए मान नहीं)।


5
  • टाइप किए गए संग्रह - भले ही आप उनका उपयोग नहीं करना चाहते हों, लेकिन आपको उनके साथ अन्य पुस्तकालयों, अन्य स्रोतों से निपटने की संभावना है।

  • कक्षा निर्माण में सामान्य टाइपिंग:

    सार्वजनिक वर्ग Foo <T> {सार्वजनिक T get () ...

  • कास्टिंग से परहेज - मुझे हमेशा से नापसंद चीजें पसंद हैं

    नए तुलनित्र {सार्वजनिक int तुलना (ऑब्जेक्ट ओ) {अगर (ओ इंस्टोफ़ क्लासइलेक्ट्रॉनिक के बारे में) ...

जहाँ आप अनिवार्य रूप से ऐसी स्थिति की जाँच कर रहे हैं जो केवल मौजूद होनी चाहिए क्योंकि इंटरफ़ेस वस्तुओं के संदर्भ में व्यक्त किया गया है।

जेनेरिक के लिए मेरी प्रारंभिक प्रतिक्रिया आपके समान थी - "बहुत गन्दा, बहुत जटिल"। मेरा अनुभव यह है कि बिट के लिए उपयोग करने के बाद आप उनके लिए अभ्यस्त हो जाते हैं, और उनके बिना कोड स्पष्ट रूप से निर्दिष्ट कम महसूस होता है, और बस कम आरामदायक होता है। उस के अलावा, जावा दुनिया के बाकी उन्हें उपयोग करता है ताकि आप कार्यक्रम के साथ अंत में, सही पाने के लिए जा रहे हैं?


1
सूर्य से बचने के संबंध में सटीक होना: कास्टिंग, सूर्य प्रलेखन के अनुसार होता है, लेकिन यह संकलक द्वारा किया जाता है। आपको बस अपने कोड में लिखने से बचना होगा।
डेमी

शायद, मैं 1.4 से ऊपर जाने के लिए अनिच्छुक कंपनियों के एक लंबे तार में फंसता हुआ प्रतीत होता हूं, इसलिए कौन जानता है, मैं 5 से पहले रिटायर हो सकता हूं। मैं भी हाल ही में सोच रहा हूं कि एक "सिम्पलजावा" को बनाना एक दिलचस्प अवधारणा हो सकती है - जावा सुविधाओं को जोड़ने के लिए जा रहा है और बहुत सारे कोड मैंने देखे हैं, यह एक स्वस्थ चीज नहीं होगी। मैं पहले से ही उन लोगों से निपटता हूं जो लूप कोड नहीं कर सकते हैं - मैं उनसे नफरत करने के लिए जेनरिक को अनियंत्रित लूप में इंजेक्ट करने की कोशिश करना पसंद करूंगा।
बिल के

@ बिल के: कुछ लोगों को जेनरिक की पूरी शक्ति का उपयोग करने की आवश्यकता होती है। यह केवल तभी आवश्यक है जब आप एक ऐसा वर्ग लिख रहे हों जो स्वयं सामान्य हो।
एडी

5

एक अच्छा उदाहरण देने के लिए। कल्पना कीजिए कि आपके पास फू नामक एक वर्ग है

public class Foo
{
   public string Bar() { return "Bar"; }
}

उदाहरण 1 अब आप फू वस्तुओं का संग्रह करना चाहते हैं। आपके पास दो विकल्प हैं, LIst या ArrayList, ये दोनों समान तरीके से काम करते हैं।

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

उपरोक्त कोड में, अगर मैं फू के बजाय फायरट्रेक के एक वर्ग को जोड़ने की कोशिश करता हूं, तो ArrayList इसे जोड़ देगा, लेकिन फू की जेनरिक सूची एक अपवाद को फेंक देगी।

उदाहरण दो।

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

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}

मैं अनुमान लगा रहा हूं कि आपने प्रश्न पढ़ने से पहले उत्तर दिया था। वे दो मामले हैं जो वास्तव में मेरी बहुत मदद नहीं करते हैं (प्रश्न में वर्णित)। क्या अन्य मामले हैं जहां यह कुछ उपयोगी है?
बिल के

4

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

क्या आप कभी नहीं चाहते हैं कि आप कट-एन-पेस्ट पुन: उपयोग करने या मजबूत-टाइपिंग से समझौता किए बिना अल्गर्थ / कोड का पुन: उपयोग कर सकें (उदाहरण के लिए मैं Listस्ट्रिंग्स चाहता हूं , Listचीजों की नहीं जो मुझे आशा है कि तार हैं!)।

इसलिए आपको जेनेरिक (या कुछ बेहतर) का उपयोग करना चाहिए ।


मुझे इस तरह के सामान के लिए पुन: कॉपी / पेस्ट करने की आवश्यकता नहीं है (यकीन नहीं कि ऐसा कैसे होगा?) आप एक इंटरफ़ेस लागू करते हैं जो आपकी आवश्यकताओं को फिट करता है, और उस इंटरफ़ेस का उपयोग करता है। दुर्लभ मामला जहां आप ऐसा नहीं कर सकते हैं, जब आप एक शुद्ध उपयोगिता (जैसे संग्रह) लिख रहे हैं, और उन लोगों के लिए (जैसा कि मैंने प्रश्न में वर्णित है) यह वास्तव में कभी समस्या नहीं रही है। मैं मजबूत टाइपिंग से समझौता करता हूं - ठीक वैसे ही जैसे आपको प्रतिबिंब का उपयोग करना है। क्या आप प्रतिबिंब का उपयोग करने से पूरी तरह से इंकार करते हैं क्योंकि आपके पास मजबूत टाइपिंग नहीं हो सकती है? (यदि मुझे प्रतिबिंब का उपयोग करना है, तो मैं इसे एक संग्रह की तरह संलग्न करता हूं)।
बिल K

3

यह मत भूलो कि जेनरिक केवल कक्षाओं द्वारा उपयोग नहीं किए जाते हैं, उनका उपयोग विधियों द्वारा भी किया जा सकता है। उदाहरण के लिए, निम्नलिखित स्निपेट लें:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

यह सरल है, लेकिन बहुत सुरुचिपूर्ण ढंग से उपयोग किया जा सकता है। अच्छी बात यह है कि यह विधि जो कुछ भी थी उसे वापस लौटा देती है। यह तब मदद करता है जब आप ऐसे अपवादों को संभाल रहे होते हैं जिन्हें कॉल करने वाले को वापस फेंकने की आवश्यकता होती है:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

मुद्दा यह है कि आप एक विधि से गुजरने के द्वारा प्रकार को नहीं खोते हैं। आप केवल एक के बजाय सही प्रकार के अपवाद को फेंक सकते हैं Throwable, जो कि आप सभी जेनरिक के बिना कर सकते हैं।

यह सामान्य तरीकों के लिए एक उपयोग का सिर्फ एक सरल उदाहरण है। जेनेरिक विधियों के साथ कुछ अन्य साफ-सुथरी चीजें हैं। मेरे विचार में सबसे अच्छे, जेनेरिक के साथ जिक्र है। निम्नलिखित उदाहरण लें (जोश बलोच के प्रभावी जावा 2 संस्करण से लिया गया):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

यह बहुत कुछ नहीं करता है, लेकिन सामान्य प्रकार लंबे (या नेस्टेड; यानी Map<String, List<String>>) होने पर यह कुछ अव्यवस्थाओं में कटौती करता है ।


मुझे नहीं लगता कि अपवादों के बारे में यह सच है। यह मुझे सोच रहा है, लेकिन मुझे 95% यकीन है कि आपको जेनेरिक (और स्पष्ट कोड) का उपयोग किए बिना बिल्कुल वही परिणाम प्राप्त होंगे। टाइप जानकारी वस्तु का हिस्सा है, न कि आप इसे किस चीज में डालते हैं। मैं इसे एक कोशिश देने के लिए ललचा रहा हूँ - हो सकता है कि मैं कल काम पर निकल जाऊं और आपके पास वापस आ जाऊं .. आपको बताऊं कि, मैं इसे आजमाने जा रहा हूं और अगर आप सही हैं, तो मैं जा रहा हूं आपकी पोस्ट की सूची और आपके 5 पदों के लिए मतदान :) यदि नहीं, तो आप इस पर विचार करना चाह सकते हैं कि जेनरिक की सरल उपलब्धता के कारण आपको अपना कोड बिना किसी लाभ के जटिल हो गया है।
बिल के

यह सच है कि यह अतिरिक्त जटिलता जोड़ता है। हालाँकि, मुझे लगता है कि इसका कुछ उपयोग है। समस्या यह है कि आप एक सादे Throwableनिकाय को किसी विधि निकाय के भीतर से नहीं फेंक सकते हैं जिसमें विशिष्ट घोषित अपवाद हैं। विकल्प या तो प्रत्येक अपवाद प्रकार को वापस करने के लिए अलग-अलग तरीके लिख रहा है, या कलाकारों को गैर-जेनेरिक विधि के साथ कर रहा है जो एक रिटर्न देता है Throwable। पूर्व बहुत क्रियात्मक है और काफी बेकार है और बाद वाले को संकलक से कोई मदद नहीं मिलेगी। जेनरिक का उपयोग करके, कंपाइलर आपके लिए सही कास्ट डालेगा। तो, सवाल यह है: क्या यह जटिलता के लायक है?
जिगौत

अच्छा मैं समझा। इसलिए यह कलाकारों के सामने आने से बचता है ... अच्छी बात है। I 5. आप पर बकाया है।
बिल K

2

मिकेल के रूप में प्राथमिक लाभ, कई वर्गों को परिभाषित करने की आवश्यकता के बिना मजबूत-टाइपिंग है।

इस तरह आप कर सकते हैं सामान:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

जेनरिक के बिना, आपको अपने कार्यों तक पहुंचने के लिए ब्लाह [0] को सही प्रकार से डालना होगा।


पसंद को देखते हुए, मैं कास्ट की बजाय कास्ट नहीं करना चाहता था।
MrBoJangles

मजबूत टाइपिंग आसानी से जेनरिक imho का सबसे अच्छा पहलू है, विशेष रूप से संकलन-समय प्रकार की जाँच जो अनुमति देता है।
ljs

2

jvm वैसे भी डालती है ... यह स्पष्ट रूप से कोड बनाता है जो सामान्य प्रकार को "ऑब्जेक्ट" के रूप में मानता है और वांछित तात्कालिकता के लिए जाति बनाता है। जावा जेनरिक सिंटैक्टिक शुगर है।


2

मुझे पता है कि यह एक सी # प्रश्न है, लेकिन अन्य भाषाओं में भी जेनेरिक का उपयोग किया जाता है, और उनके उपयोग / लक्ष्य काफी समान हैं।

जावा संग्रह जावा 1.5 के बाद से जेनरिक का उपयोग करता है । इसलिए, उनका उपयोग करने के लिए एक अच्छी जगह तब है जब आप अपना संग्रह जैसी वस्तु बना रहे हों।

एक उदाहरण जो मुझे लगभग हर जगह दिखाई देता है वह एक जोड़ी वर्ग है, जो दो वस्तुओं को रखता है, लेकिन उन वस्तुओं के साथ एक सामान्य तरीके से निपटने की जरूरत है।

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

जब भी आप इस जोड़ी वर्ग का उपयोग करते हैं, तो आप निर्दिष्ट कर सकते हैं कि आप किस प्रकार की वस्तुओं से निपटना चाहते हैं और रन टाइम के बजाय किसी भी प्रकार की कास्ट समस्याओं का संकलन समय पर दिखाई देगा।

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

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

हालांकि यह अपने आप में बहुत दिलचस्प नहीं है, यह जानना उपयोगी है कि जब भी आप एक FooManager के साथ व्यवहार करते हैं, तो आप जानते हैं कि यह MyClass प्रकार को संभाल लेगा, और MyClass Foo का विस्तार करता है।


2

सन जावा प्रलेखन से, "मुझे जेनेरिक का उपयोग क्यों करना चाहिए?" के जवाब में।

"जेनरिक आपके लिए संकलनकर्ता के संग्रह के प्रकार को संप्रेषित करने का एक तरीका प्रदान करता है, ताकि इसकी जाँच की जा सके। एक बार जब संकलनकर्ता को संग्रह के तत्व प्रकार का पता चल जाता है, तो संकलक जाँच सकता है कि आपने संग्रह का लगातार उपयोग किया है और सम्मिलित कर सकते हैं। संग्रह से निकाले जा रहे मूल्यों पर सही कास्ट ... जेनेरिक का उपयोग करने वाला कोड स्पष्ट और सुरक्षित है .... संकलक संकलन समय पर सत्यापित कर सकता है कि प्रकार की बाधाएं चलाने के समय [जोर मेरा] पर उल्लंघन नहीं किया गया है । कार्यक्रम चेतावनी के बिना संकलित करता है, हम निश्चितता के साथ कह सकते हैं कि यह क्लासकस्टेसेप्शन को रन टाइम पर नहीं फेंकेगा। विशेष रूप से बड़े कार्यक्रमों में जेनरिक का उपयोग करने का शुद्ध प्रभाव, पठनीयता और मजबूती में सुधार होता है । [जोर मेरा]


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

@ बिल K: यदि कोड को कसकर नियंत्रित किया जाता है (जो कि, केवल आपके द्वारा उपयोग किया जाता है) तो शायद आप कभी भी इस मुद्दे के रूप में सामने नहीं आएंगे। एक दम बढ़िया! सबसे बड़ी जोखिम बड़ी परियोजनाओं में है जो कई लोग आईएमओ पर काम करते हैं। यह एक सुरक्षा जाल है, और एक "सर्वोत्तम अभ्यास" है।
डेमी

@ बिल के: यहां एक उदाहरण है। कहते हैं कि आपके पास एक ऐसा तरीका है जो एक ArrayList देता है। मान लीजिए कि ArrayList एक सामान्य संग्रह नहीं है। किसी का कोड इस ArrayList को लेता है और इसके आइटम पर कुछ ऑपरेशन करने का प्रयास करता है। एकमात्र समस्या यह है कि आपने ArrayList को BillTypes से भर दिया, और उपभोक्ता ConsumerTypes पर काम करना चाह रहा है। यह संकलन करता है, लेकिन रनटाइम के दौरान उड़ा देता है। यदि आप सामान्य संग्रह का उपयोग करते हैं, तो आपके पास संकलक त्रुटियां होंगी, जो निपटने के लिए बहुत अच्छे हैं।
डेमी

@ बिल के: मैंने व्यापक स्तर के कौशल स्तरों वाले लोगों के साथ काम किया है। मेरे पास चुनने की विलासिता नहीं है कि मैं किसके साथ परियोजना साझा करूं। इस प्रकार, प्रकार की सुरक्षा मेरे लिए बहुत महत्वपूर्ण है। हां, मैंने जेनरिक का उपयोग करने के लिए कोड परिवर्तित करके (और सही किया) बग ढूंढ लिया है।
एडी

1

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

जेनेरिक सूची का उपयोग करके आप एक सूची सूची सूची जो आप चाहते हैं और आप हमेशा मजबूत टाइपिंग को संदर्भित कर सकते हैं, आपको कन्वर्ट करने की ज़रूरत नहीं है या कुछ भी नहीं जैसे आप एक ऐरे या मानक सूची के साथ करेंगे।


1

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

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


1

यदि आपके संग्रह में मूल्य प्रकार हैं, तो उन्हें संग्रह में सम्मिलित किए जाने पर ऑब्जेक्ट्स को बॉक्स / अनबॉक्स करने की आवश्यकता नहीं है, ताकि आपका प्रदर्शन नाटकीय रूप से बढ़ जाए। रिचार्पर जैसे कूल ऐड-ऑन आपके लिए अधिक कोड उत्पन्न कर सकते हैं, जैसे कि फ़ोरच लूप।


1

जेनरिक (विशेषकर कलेक्शंस / लिस्ट के साथ) का उपयोग करने का एक और फायदा आपको कंपाइल टाइम टाइप चेकिंग से मिलता है। वस्तुओं की सूची के बजाय सामान्य सूची का उपयोग करते समय यह वास्तव में उपयोगी है।


1

एकल सबसे अधिक कारण वे टाइप सुरक्षा प्रदान करते हैं

List<Customer> custCollection = new List<Customer>;

विरोध के रूप में,

object[] custCollection = new object[] { cust1, cust2 };

एक साधारण उदाहरण के रूप में।


टाइप सुरक्षा, अपने आप में एक चर्चा। शायद किसी को इसके बारे में एक सवाल पूछना चाहिए।
MrBoJangles

हाँ, लेकिन आप क्या इशारा कर रहे हैं? पूरे प्रकार की सुरक्षा?
विन

1

सारांश में, जेनेरिक आपको अधिक प्राथमिक रूप से निर्दिष्ट करने की अनुमति देते हैं कि आप क्या करने का इरादा रखते हैं (मजबूत टाइपिंग)।

आपके लिए इसके कई लाभ हैं:

  • क्योंकि कंपाइलर को पता है कि आप क्या करना चाहते हैं, यह आपको कई प्रकार के कास्टिंग को छोड़ने की अनुमति देता है क्योंकि यह पहले से ही जानता है कि प्रकार संगत होगा।

  • इससे आपको अपने प्रोग्राम के सही होने के बारे में पहले की प्रतिक्रिया भी मिलती है। ऐसी चीजें जो पहले रनटाइम में विफल हो जाती थीं (जैसे कि कोई वस्तु वांछित प्रकार में डाली नहीं जा सकती थी), अब संकलन-समय पर विफल हो जाती है और आप अपने परीक्षण-विभाग द्वारा एक गुप्त बग रिपोर्ट दर्ज करने से पहले गलती को ठीक कर सकते हैं।

  • कंपाइलर अधिक अनुकूलन कर सकता है, जैसे मुक्केबाजी से बचना आदि।


1

जोड़ने / विस्तार करने के लिए चीजों की एक जोड़ी (देखने के .NET बिंदु से बोल):

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

विधियों पर सामान्य तर्क एक ही काम कर सकते हैं, लेकिन वे कास्टिंग करने के लिए "बताओ मत पूछो" सिद्धांत को लागू करने में भी मदद करते हैं, अर्थात "मुझे वह दें जो मैं चाहता हूं, और यदि आप नहीं कर सकते, तो आप मुझे बताएं"।


1

मैं उदाहरण के लिए उनका उपयोग स्प्रिंगडाइम और हाइबरनेट के साथ लागू एक जेनरिकडाओ में करता हूं जो इस तरह दिखता है

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

इस डीएओ के मेरे कार्यान्वयन को जेनरिक का उपयोग करके डेवलपर को उन इकाइयों को पारित करने के लिए मजबूर करता है, जिन्हें वे केवल जेनरिकडॉ उप-वर्ग के द्वारा डिजाइन किए गए हैं।

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

मेरी छोटी रूपरेखा अधिक मजबूत है (छानने, आलसी लोडिंग, खोज जैसी चीजें हैं)। मैंने आपको उदाहरण देने के लिए यहां सरलीकरण किया है

मैं, स्टीव और आप की तरह, शुरुआत में कहा "बहुत गन्दा और जटिल" लेकिन अब मुझे इसके फायदे दिखाई दे रहे हैं


1

स्पष्ट रूप से "टाइप सेफ्टी" और "नो कास्टिंग" जैसे लाभों का पहले ही उल्लेख किया गया है, इसलिए शायद मैं कुछ अन्य "लाभों" के बारे में बात कर सकता हूं जो मुझे आशा है कि यह मदद करता है।

सबसे पहले, जेनेरिक एक भाषा-स्वतंत्र अवधारणा है और, आईएमओ, यदि आप एक ही समय में नियमित (रनटाइम) बहुरूपता के बारे में सोचते हैं तो यह अधिक समझ में आता है।

उदाहरण के लिए, जैसा कि हम ऑब्जेक्ट ओरिएंटेड डिज़ाइन से जानते हैं, बहुरूपता में रनटाइम नोटिफ़िकेशन होता है, जहाँ रनटाइम पर कॉलर ऑब्जेक्ट का पता लगाया जाता है, क्योंकि प्रोग्राम एक्जीक्यूशन जाता है और संबंधित विधि को रनटाइम प्रकार के आधार पर कहा जाता है। जेनरिक में, विचार कुछ हद तक समान है लेकिन सब कुछ संकलन के समय होता है। इसका क्या मतलब है और आप इसका उपयोग कैसे करते हैं?

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

कक्षाएं बहुत समान तरीके से काम करती हैं। आप टाइप करते हैं और कोड कंपाइलर द्वारा जनरेट किया जाता है।

एक बार जब आप "संकलन समय" का विचार प्राप्त करते हैं, तो आप "बाउंडेड" प्रकारों का उपयोग कर सकते हैं और कक्षाओं / विधियों के माध्यम से एक पैरामीट्रिक प्रकार के रूप में पारित किया जा सकता है। तो, आप नियंत्रित कर सकते हैं कि किस माध्यम से पारित किया जाए जो एक शक्तिशाली चीज है विशेष रूप से आपके पास अन्य लोगों द्वारा खपत की जाने वाली एक रूपरेखा है।

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

कोई भी अब MyObject के अलावा sth सेट नहीं कर सकता है।

इसके अलावा, आप अपने तरीके के तर्कों पर "बाधाओं को लागू" कर सकते हैं जिसका अर्थ है कि आप यह सुनिश्चित कर सकते हैं कि आपके दोनों विधि तर्क एक ही प्रकार पर निर्भर होंगे।

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

आशा है कि यह सब समझ में आता है।



0

संग्रह के लिए जेनरिक का उपयोग करना सरल और साफ है। यहां तक ​​कि अगर आप इसे हर जगह पर पंट करते हैं, तो संग्रह से प्राप्त लाभ मेरे लिए एक जीत है।

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

बनाम

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

या

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

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


1
जेनेरिक के बिना आप कर सकते हैं: सामान की सूची दें = getStuff (); के लिए (वस्तु सामान: सामान)) ((सामान) सामान) .doStuff (); }, जो बहुत अलग नहीं है।
टॉम हॉकिन -

जेनेरिक के बिना मैं stuffList.doAll (); मैंने कहा कि मेरे पास हमेशा एक रैपर क्लास है जो बिल्कुल उसी तरह कोड के लिए जगह है। अन्यथा आप इस लूप को कहीं और कॉपी करने से कैसे बचते हैं? आप इसे पुन: उपयोग के लिए कहां रखते हैं? रैपर क्लास में शायद किसी तरह का एक लूप होगा, लेकिन उस क्लास में, चूंकि वह क्लास "द स्टफ" के बारे में है, इसलिए लूप के लिए एक कास्टेड का उपयोग करते हुए टॉम जैसा कि ऊपर सुझाया गया है, वह बहुत स्पष्ट / सेल्फ डॉक्यूमेंटिंग है।
बिल के

@ जेरिको, यह वास्तव में दिलचस्प है। मुझे आश्चर्य है कि अगर कोई कारण है कि आप ऐसा महसूस करते हैं, और अगर ऐसा है जो मुझे समझ नहीं आ रहा है - कि लोगों की प्रकृति में कुछ है जो किसी भी कारण से किसी भी तरह से अश्लील किसी भी कास्टिंग पाता है और अप्राकृतिक लंबाई में जाने के लिए तैयार है (जैसे इससे बचने के लिए बहुत अधिक जटिल वाक्यविन्यास जोड़ना)। क्यों "यदि आपको कुछ डालना है, तो आप पहले से ही खो चुके हैं"?
बिल के

@ जेरिको: सन डॉक्यूमेंटेशन के अनुसार, जेनरिक कंपाइलर को इस बात का ज्ञान देता है कि वस्तुओं को किस चीज से जोड़ा जाए। चूंकि कंपाइलर उपयोगकर्ता के बजाय जेनरिक के लिए कास्टिंग करता है, क्या यह आपके कथन को बदलता है?
डेमी

मैंने कोड को प्री जावा 1.5 से जेनरिक में बदल दिया है, और इस प्रक्रिया में बग पाए गए जहां गलत ऑब्जेक्ट प्रकार को एक संग्रह में रखा गया था। मैं "अगर आपको कास्ट करना है तो आप कैंप खो चुके हैं" में गिर जाते हैं, क्योंकि अगर आपको मैन्युअल रूप से कास्ट करना है, तो आप क्लासकैस्ट एक्ससेप्शन का जोखिम उठाते हैं। जेनेरिक्स के साथ संकलक यह आप के लिए अदृश्य रूप से करता है, लेकिन एक ClassCastException का मौका है जिस तरह से प्रकार सुरक्षा की वजह से कम कर दिया। हाँ, जेनेरिक के बिना आप ऑब्जेक्ट का उपयोग कर सकते हैं, लेकिन यह मेरे लिए एक भयानक कोड गंध है, "थ्रोसेप्शन एक्सेप्शन"।
एडी

0

जेनरिक आपको विशिष्ट प्रकार का समर्थन प्रदान करते हुए अधिक पुन: प्रयोज्य वस्तुओं / विधियों को बनाने की क्षमता भी देता है। आप कुछ मामलों में बहुत अधिक प्रदर्शन प्राप्त करते हैं। मैं जावा जेनरिक पर पूर्ण युक्ति नहीं जानता, लेकिन .NET में मैं टाइप पैरामीटर पर अवरोधों को निर्दिष्ट कर सकता हूं, जैसे कि इम्प्लीमेंट्स ए इंटरफ़ेस, कंस्ट्रक्टर, और व्युत्पत्ति।


0
  1. जेनेरिक एल्गोरिदम को लागू करने के लिए प्रोग्रामर को सक्षम करना - जेनेरिक का उपयोग करके, प्रोग्रामर जेनेरिक एल्गोरिदम को लागू कर सकते हैं जो विभिन्न प्रकारों के संग्रह पर काम करते हैं, अनुकूलित किए जा सकते हैं, और टाइप-सुरक्षित और पढ़ने में आसान हैं।

  2. संकलित समय पर मजबूत प्रकार की जांच - एक जावा संकलक सामान्य कोड की जाँच करने के लिए मजबूत प्रकार लागू करता है और यदि कोड प्रकार की सुरक्षा का उल्लंघन करता है तो त्रुटियों को जारी करता है। संकलन समय त्रुटियों को ठीक करना रनटाइम त्रुटियों को ठीक करने से आसान है, जिसे ढूंढना मुश्किल हो सकता है।

  3. जातियों का उन्मूलन।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.