इसके कारण जावा जेनरिक कैसे लागू होते हैं, इस पर आधारित हैं।
एक उदाहरण उदाहरण
सरणियों के साथ आप ऐसा कर सकते हैं (सरणियाँ सहवर्ती हैं)
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
लेकिन, अगर आप ऐसा करने की कोशिश करेंगे तो क्या होगा?
myNumber[0] = 3.14; //attempt of heap pollution
यह अंतिम पंक्ति ठीक-ठीक संकलन करेगी, लेकिन यदि आप इस कोड को चलाते हैं, तो आप ए ArrayStoreException
। क्योंकि आप एक पूर्णांक सरणी में एक संख्या डालने की कोशिश कर रहे हैं (एक नंबर संदर्भ के माध्यम से पहुँचा जा रहा है)।
इसका मतलब है कि आप संकलक को मूर्ख बना सकते हैं, लेकिन आप रनटाइम प्रकार प्रणाली को मूर्ख नहीं बना सकते। और ऐसा इसलिए है क्योंकि सरणियाँ वे हैं जिन्हें हम रिफ़ेक्टिव प्रकार कहते हैं । इसका मतलब यह है कि रनटाइम में जावा को पता है कि यह सरणी वास्तव में पूर्णांकों की एक सरणी के रूप में तात्कालिक थी, जो कि केवल प्रकार के संदर्भ के माध्यम से एक्सेस की जाती है Number[]
।
तो, जैसा कि आप देख सकते हैं, एक चीज वस्तु का वास्तविक प्रकार है, और दूसरी चीज उस संदर्भ का प्रकार है जिसे आप इसे एक्सेस करने के लिए उपयोग करते हैं, है ना?
जावा पीढ़ी के साथ समस्या
अब, जावा जेनेरिक प्रकारों के साथ समस्या यह है कि टाइप जानकारी को कंपाइलर द्वारा छोड़ दिया जाता है और यह रन टाइम पर उपलब्ध नहीं है। इस प्रक्रिया को टाइप इरेज़र कहा जाता है । जावा में इस तरह की जेनरिक को लागू करने के लिए अच्छे कारण हैं, लेकिन यह एक लंबी कहानी है, और इसे अन्य चीजों के अलावा, पूर्व-मौजूदा कोड के साथ द्विआधारी संगतता के साथ करना है (देखें कि हमें जो जेनरिक मिला है वह कैसे है )।
लेकिन यहां महत्वपूर्ण बिंदु यह है कि चूंकि, रनटाइम के दौरान कोई प्रकार की जानकारी नहीं है, इसलिए यह सुनिश्चित करने का कोई तरीका नहीं है कि हम गंभीर प्रदूषण नहीं कर रहे हैं।
उदाहरण के लिए,
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution
यदि जावा कंपाइलर आपको ऐसा करने से नहीं रोकता है, तो रनटाइम टाइप सिस्टम आपको रोक भी नहीं सकता है, क्योंकि रनटाइम पर कोई रास्ता नहीं है, यह निर्धारित करने के लिए कि यह सूची केवल पूर्णांकों की सूची होनी चाहिए थी। जावा रनटाइम आपको इस सूची में जो कुछ भी चाहिए, वह आपको तब डालने देगा, जब इसमें केवल पूर्णांक होना चाहिए, क्योंकि जब इसे बनाया गया था, तो यह पूर्णांकों की सूची के रूप में घोषित किया गया था।
जैसे, जावा के डिजाइनरों ने सुनिश्चित किया कि आप कंपाइलर को बेवकूफ नहीं बना सकते। यदि आप संकलक को बेवकूफ नहीं बना सकते हैं (जैसा कि हम सरणियों के साथ कर सकते हैं) तो आप रनटाइम प्रकार प्रणाली को भी मूर्ख नहीं बना सकते हैं।
इस प्रकार, हम कहते हैं कि सामान्य प्रकार गैर-परिवर्तनीय हैं ।
जाहिर है, यह बहुरूपता को बाधित करेगा। निम्नलिखित उदाहरण पर विचार करें:
static long sum(Number[] numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
अब आप इसे इस तरह इस्तेमाल कर सकते हैं:
Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};
System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));
लेकिन अगर आप सामान्य कोड के साथ समान कोड लागू करने का प्रयास करते हैं, तो आप सफल नहीं होंगे:
static long sum(List<Number> numbers) {
long summation = 0;
for(Number number : numbers) {
summation += number.longValue();
}
return summation;
}
यदि आप ...
List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);
System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error
इसका समाधान जावा जेनेरिक की दो शक्तिशाली विशेषताओं का उपयोग करना सीखना है, जिन्हें कोवरियन और कंट्रोवर्सी के रूप में जाना जाता है।
सहप्रसरण
सहसंयोजक के साथ आप एक संरचना से आइटम पढ़ सकते हैं, लेकिन आप इसमें कुछ भी नहीं लिख सकते हैं। ये सभी वैध घोषणाएं हैं।
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();
और आप इससे पढ़ सकते हैं myNums
:
Number n = myNums.get(0);
क्योंकि आप यह सुनिश्चित कर सकते हैं कि वास्तविक सूची में जो कुछ भी है, उसे एक संख्या तक सीमित किया जा सकता है (आखिर कुछ भी जो संख्या का विस्तार करता है वह संख्या है, सही?)
हालाँकि, आपको एक सहसंयोजक संरचना में कुछ भी डालने की अनुमति नहीं है।
myNumst.add(45L); //compiler error
यह अनुमति नहीं दी जाएगी, क्योंकि जावा यह गारंटी नहीं दे सकता है कि जेनेरिक संरचना में वस्तु का वास्तविक प्रकार क्या है। यह कुछ भी हो सकता है जो संख्या का विस्तार करता है, लेकिन संकलक निश्चित नहीं हो सकता है। इसलिए आप पढ़ सकते हैं, लेकिन लिख नहीं सकते।
contravariance
विपरीतता के साथ आप इसके विपरीत कर सकते हैं। आप चीजों को एक सामान्य संरचना में रख सकते हैं, लेकिन आप इसे पढ़ नहीं सकते।
List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
इस मामले में, वस्तु की वास्तविक प्रकृति वस्तुओं की एक सूची है, और विरोधाभासी के माध्यम से, आप संख्याओं को इसमें डाल सकते हैं, मूल रूप से क्योंकि सभी संख्याओं में उनके सामान्य पूर्वजों के रूप में ऑब्जेक्ट हैं। जैसे, सभी संख्याएँ ऑब्जेक्ट हैं, और इसलिए यह मान्य है।
हालाँकि, आप इस संदर्भात्मक संरचना से सुरक्षित रूप से कुछ भी नहीं पढ़ सकते हैं यह मानते हुए कि आपको एक नंबर मिलेगा।
Number myNum = myNums.get(0); //compiler-error
जैसा कि आप देख सकते हैं, यदि संकलक ने आपको यह पंक्ति लिखने की अनुमति दी है, तो आपको रनटाइम पर एक क्लासकैस्ट अपवाद मिलेगा।
जाओ / सिद्धांत रखो
इस प्रकार, सहसंयोजक का उपयोग करें जब आप केवल एक संरचना से जेनेरिक मान लेने का इरादा रखते हैं, तो कॉन्ट्रोवर्सी का उपयोग करें जब आप केवल एक संरचना में जेनेरिक मान रखने का इरादा रखते हैं और सटीक जेनेरिक प्रकार का उपयोग करते हैं जब आप दोनों करने का इरादा रखते हैं।
मेरे पास सबसे अच्छा उदाहरण निम्नलिखित है जो किसी भी प्रकार की संख्याओं को एक सूची से दूसरी सूची में कॉपी करता है। यह केवल हो जाता है स्रोत से आइटम, और यह केवल डालता लक्ष्य में आइटम नहीं है।
public static void copy(List<? extends Number> source, List<? super Number> target) {
for(Number number : source) {
target(number);
}
}
इस तरह के मामले के लिए सहसंयोजक और विरोधाभासी की शक्तियों के लिए यह काम करता है:
List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);