जावा जेनरिक जावा भाषा की सबसे महत्वपूर्ण विशेषताओं में से एक है। जेनरिक के पीछे का विचार काफी सरल है, हालांकि, इसके साथ जुड़े सामान्य सिंटैक्स से बदलाव के कारण कभी-कभी यह जटिल हो जाता है।
इस ट्यूटोरियल का उद्देश्य आपको आसानी से समझने योग्य तरीके से जेनरिक की इस उपयोगी अवधारणा से परिचित कराना है।
लेकिन जेनेरिक में गोता लगाने से पहले, आइए जानें कि सबसे पहले जावा जेनरिक की आवश्यकता क्यों थी।
जावा जेनरिक का उद्देश्य
जावा 5 में जेनरिक की शुरुआत से पहले, आप बिना किसी त्रुटि या चेतावनी के कोड स्निपेट लिख और संकलित कर सकते थे:
List list = new ArrayList();
list.add("hey");
list.add(new Object());
आप किसी सूची या किसी अन्य जावा संग्रह में किसी भी प्रकार के मूल्यों को यह घोषित किए बिना जोड़ सकते हैं कि यह किस प्रकार का डेटा संग्रहीत करता है। लेकिन जब आप सूची से मान प्राप्त करते हैं, तो आपको इसे स्पष्ट रूप से एक निश्चित प्रकार में डालना होगा।
उपरोक्त सूची के माध्यम से पुनरावृति पर विचार करें।
for (int i=0; i< list.size(); i++) {
String value = (String) list.get(i); //CastClassException when i=1
}
पहले स्टोर किए गए डेटा प्रकार की घोषणा किए बिना एक सूची बनाने की अनुमति देने से, जैसा कि हमने किया था, इसके परिणामस्वरूप प्रोग्रामर ऊपर की तरह गलतियां कर सकते हैं जो ClassCastExceptions
फेंकता है रनटाइम के दौरान।
प्रोग्रामर को ऐसी गलतियाँ करने से रोकने के लिए जेनरिक पेश किए गए थे।
जेनरिक के साथ, आप स्पष्ट रूप से उस डेटा प्रकार की घोषणा कर सकते हैं जिसे जावा संग्रह बनाते समय संग्रहीत किया जा रहा है, जैसा कि निम्न उदाहरण से पता चलता है।
नोट:आप अभी भी संग्रहीत डेटा प्रकार निर्दिष्ट किए बिना जावा संग्रह ऑब्जेक्ट बना सकते हैं लेकिन इसकी अनुशंसा नहीं की जाती है।List<String> stringList = new ArrayList<>();
अब, आप संकलन-समय त्रुटि को फेंके बिना गलती से एक पूर्णांक को स्ट्रिंग प्रकार की सूची में संग्रहीत नहीं कर सकते। यह सुनिश्चित करता है कि आपका प्रोग्राम रनटाइम त्रुटियों में नहीं चलता है।
stringList.add(new Integer(4)); //Compile time Error
जावा में जेनरिक की शुरूआत का मुख्य उद्देश्य ClassCastExceptions
. में चलने से बचना था रनटाइम के दौरान।
Java Generics बनाना
आप जावा कक्षाओं और विधियों को बनाने के लिए जेनरिक का उपयोग कर सकते हैं। आइए प्रत्येक प्रकार के जेनरिक बनाने के उदाहरण देखें।
जेनेरिक क्लास
एक सामान्य वर्ग बनाते समय, वर्ग के नाम के अंत में कोण के भीतर वर्ग के लिए प्रकार पैरामीटर जोड़ा जाता है <>
कोष्ठक।
public class GenericClass<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return this.item;
}
}
यहां, T
डेटा प्रकार पैरामीटर है। T
, N
, और E
जावा सम्मेलनों के अनुसार डेटा प्रकार मापदंडों के लिए उपयोग किए जाने वाले कुछ अक्षर हैं।
उपरोक्त उदाहरण में, GenericClass ऑब्जेक्ट बनाते समय आप इसे एक विशिष्ट डेटा प्रकार पास कर सकते हैं।
public static void main(String[] args) {
GenericClass<String> gc1 = new GenericClass<>();
gc1.setItem("hello");
String item1 = gc1.getItem(); // "hello"
gc1.setItem(new Object()); //Error
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setItem(new Integer(1));
Integer item2 = gc2.getItem(); // 1
gc2.setItem("hello"); //Error
}
जेनेरिक क्लास ऑब्जेक्ट बनाते समय आप एक आदिम डेटा प्रकार को डेटा प्रकार पैरामीटर में पास नहीं कर सकते। केवल ऑब्जेक्ट प्रकार का विस्तार करने वाले डेटा प्रकारों को प्रकार पैरामीटर के रूप में पारित किया जा सकता है।
उदाहरण के लिए:
GenericClass<float> gc3 = new GenericClass<>(); //Error
जेनेरिक तरीके
सामान्य तरीके बनाना सामान्य वर्ग बनाने के समान पैटर्न का अनुसरण करता है। आप एक सामान्य वर्ग के साथ-साथ एक गैर-सामान्य वर्ग के अंदर एक सामान्य विधि लागू कर सकते हैं।
public class GenericMethodClass {
public static <T> void printItems(T[] arr){
for (int i=0; i< arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void main(String[] args) {
String[] arr1 = {"Cat", "Dog", "Mouse"};
Integer[] arr2 = {1, 2, 3};
GenericMethodClass.printItems(arr1); // "Cat", "Dog", "Mouse"
GenericMethodClass.printItems(arr2); // 1, 2, 3
}
}
यहां, आप विधि को पैरामीटर करने के लिए एक विशिष्ट प्रकार की सरणी पास कर सकते हैं। सामान्य विधि PrintItems()
पारित सरणी के माध्यम से पुनरावृत्त होता है और सामान्य जावा विधि की तरह ही संग्रहीत वस्तुओं को प्रिंट करता है।
बाउंडेड टाइप पैरामीटर्स
अब तक, हमने ऊपर बनाए गए सामान्य वर्गों और विधियों को आदिम प्रकारों के अलावा किसी भी डेटा प्रकार के लिए मानकीकृत किया जा सकता है। लेकिन क्या होगा अगर हम उन डेटा प्रकारों को सीमित करना चाहते हैं जिन्हें जेनरिक में पारित किया जा सकता है? यहीं पर बाउंडेड टाइप पैरामीटर आते हैं।
आप किसी सामान्य वर्ग या विधि द्वारा स्वीकार किए गए डेटा प्रकारों को यह निर्दिष्ट करके बाध्य कर सकते हैं कि यह किसी अन्य डेटा प्रकार का उपवर्ग होना चाहिए।
उदाहरण के लिए:
//accepts only subclasses of List
public class UpperBoundedClass<T extends List>{
//accepts only subclasses of List
public <T extends List> void UpperBoundedMethod(T[] arr) {
}
}
यहाँ, UpperBoundedClass
और UpperBoundedMethod
केवल List
. के उप-प्रकारों का उपयोग करके पैरामीटरकृत किया जा सकता है डेटा प्रकार।
List
डेटा प्रकार प्रकार पैरामीटर के लिए ऊपरी सीमा के रूप में कार्य करता है। यदि आप ऐसे डेटा प्रकार का उपयोग करने का प्रयास करते हैं जो List
. का उप-प्रकार नहीं है , यह एक संकलन-समय त्रुटि फेंक देगा।
सीमाएं केवल कक्षाओं तक ही सीमित नहीं हैं। आप इंटरफेस भी पास कर सकते हैं। इंटरफ़ेस का विस्तार करने का अर्थ है, इस मामले में, इंटरफ़ेस को लागू करना।
एक पैरामीटर में कई सीमाएँ भी हो सकती हैं जैसे यह उदाहरण दिखाता है।
//accepts only subclasses of both Mammal and Animal
public class MultipleBoundedClass<T extends Mammal & Animal>{
//accepts only subclasses of both Mammal and Animal
public <T extends Mammal & Animal> void MultipleBoundedMethod(T[] arr){
}
}
स्वीकृत डेटा प्रकार पशु और स्तनपायी दोनों वर्गों का एक उपवर्ग होना चाहिए। यदि इनमें से कोई एक सीमा एक वर्ग है, तो उसे बाध्य घोषणा में पहले आना चाहिए।
उपरोक्त उदाहरण में, यदि स्तनपायी एक वर्ग है और पशु एक इंटरफ़ेस है, तो स्तनपायी को पहले आना चाहिए जैसा कि ऊपर दिखाया गया है। अन्यथा, कोड एक संकलन-समय त्रुटि फेंकता है।
Java Generics Wildcards
वाइल्डकार्ड का उपयोग जेनेरिक प्रकार के मापदंडों को विधियों पर पारित करने के लिए किया जाता है। एक सामान्य विधि के विपरीत, यहाँ, सामान्य पैरामीटर विधि द्वारा स्वीकार किए गए मापदंडों को पारित किया जाता है, जो कि ऊपर चर्चा किए गए डेटा प्रकार पैरामीटर से अलग है। वाइल्डकार्ड किसके द्वारा दर्शाया जाता है? प्रतीक।
public void printItems(List<?> list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
}
}
उपरोक्त printItems()
विधि पैरामीटर के रूप में किसी भी डेटा प्रकार की सूचियों को स्वीकार करती है। यह प्रोग्रामर्स को विभिन्न डेटा प्रकारों की सूचियों के लिए कोड दोहराने से रोकता है, जो कि जेनरिक के बिना होगा।
अपर बाउंडेड वाइल्डकार्ड्स
यदि हम विधि द्वारा स्वीकृत सूची में संग्रहीत डेटा प्रकारों को सीमित करना चाहते हैं, तो हम बाउंडेड वाइल्डकार्ड का उपयोग कर सकते हैं।
उदाहरण:
public void printSubTypes(List<? extends Color> list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
}
}
printSubTypes()
विधि केवल उन सूचियों को स्वीकार करती है जो रंग के उपप्रकारों को संग्रहीत करती हैं। यह RedColor या BlueColor ऑब्जेक्ट की सूची को स्वीकार करता है, लेकिन पशु ऑब्जेक्ट की सूची को स्वीकार नहीं करता है। ऐसा इसलिए है क्योंकि पशु रंग का उपप्रकार नहीं है। यह अपर-बाउंडेड वाइल्डकार्ड का एक उदाहरण है।
लोअर बाउंडेड वाइल्डकार्ड्स
इसी तरह, अगर हमारे पास:
public void printSuperTypes(List<? super Dog> list) {
for (int i=0; i< list.size(); i++) {
System.out.println(list.get(i));
}
}
फिर, printSuperTypes()
विधि केवल उन सूचियों को स्वीकार करती है जो सुपर प्रकार के डॉग क्लास को स्टोर करती हैं। यह स्तनपायी या पशु वस्तुओं की सूची को स्वीकार करेगा लेकिन लैबडॉग वस्तुओं की सूची नहीं क्योंकि लैबडॉग कुत्ते का सुपरक्लास नहीं है, बल्कि एक उपवर्ग है। यह लोअर-बाउंडेड वाइल्डकार्ड का एक उदाहरण है।
निष्कर्ष
Java Generics एक ऐसी विशेषता बन गई है जिसके परिचय के बाद से प्रोग्रामर इसके बिना नहीं रह सकते हैं।
यह लोकप्रियता प्रोग्रामर्स के जीवन को आसान बनाने पर इसके प्रभाव के कारण है। उन्हें कोडिंग की गलतियाँ करने से रोकने के अलावा, जेनरिक का उपयोग कोड को कम दोहरावदार बनाता है। क्या आपने देखा कि यह विभिन्न डेटा प्रकारों के लिए कोड दोहराने से बचने के लिए कक्षाओं और विधियों को कैसे सामान्य बनाता है?
भाषा का विशेषज्ञ बनने के लिए जेनरिक की अच्छी समझ होना जरूरी है। इसलिए, इस ट्यूटोरियल में आपने जो सीखा है उसे व्यावहारिक कोड में लागू करना अब आगे बढ़ने का तरीका है।