परिचय: वह समस्या जिसे Generics हल करते हैं
\nकल्पना करें कि आप संख्याओं की सूची को सॉर्ट करने के लिए एक मेथड लिख रहे हैं। आप इसे Integer के लिए लिखते हैं, और एक सप्ताह बाद आपको String की सूची को सॉर्ट करने की आवश्यकता होती है। आपको एक नया मेथड बनाना होगा या Object का उपयोग करना होगा और लगातार टाइप कास्टिंग करनी होगी। यह असुविधाजनक, असुरक्षित है और रनटाइम के दौरान त्रुटियों की ओर ले जाता है।
Java में Generics (जेनेरिक्स) इसी समस्या को हल करते हैं। वे क्लासेस, इंटरफेसेस और मेथड्स बनाने की अनुमति देते हैं जो विभिन्न डेटा प्रकारों के साथ काम करते हैं, लेकिन कंपाइलेशन चरण में सख्त टाइपिंग बनाए रखते हैं। जेनेरिक्स के बिना, आधुनिक Java बहुत कम सुविधाजनक और सुरक्षित होता।
\nइस लेख में, हम समझेंगे कि Generics क्यों ज़रूरी हैं, वे कैसे काम करते हैं, और सबसे महत्वपूर्ण बात, वास्तविक उदाहरणों पर उन्हें कैसे लागू करें।
\n\n1. Generics की मूल बातें: यह क्या है और कैसे काम करता है
\n1.1. "रॉ" प्रकारों (raw types) की समस्या
\nJava 5 (2004) से पहले, डेवलपर्स बिना प्रकार निर्दिष्ट किए कलेक्शन का उपयोग करते थे:
\nList list = new ArrayList();\nlist.add("Hello");\nlist.add(123);\n// प्राप्त करते समय याद रखना होता है कि कौन सा प्रकार है\nString text = (String) list.get(0); // ठीक है\nInteger num = (Integer) list.get(1); // ठीक है\n// लेकिन अगर गलती हो जाए — runtime में ClassCastException\nString error = (String) list.get(1); // त्रुटि!\nऐसा कोड खतरनाक है: त्रुटि केवल प्रोग्राम चलाने पर ही पता चलती है।
\n\n1.2. Generics कैसे काम करते हैं
\nGenerics आपको कोण कोष्ठक <T> में सीधे उस डेटा प्रकार को निर्दिष्ट करने की अनुमति देते हैं जिसके साथ क्लास या मेथड काम करेगा। कंपाइलर कंपाइलेशन चरण में प्रकारों की जाँच करता है और स्वचालित रूप से सही कास्टिंग सम्मिलित करता है।
मूल सिंटैक्स:
\n// जेनेरिक क्लास की घोषणा\npublic class Box<T> {\n private T content;\n \n public void setContent(T content) {\n this.content = content;\n }\n \n public T getContent() {\n return content;\n }\n}\n\n// उपयोग\nBox<String> stringBox = new Box<>(); // <> — डायमंड ऑपरेटर (Java 7+)\nstringBox.setContent("नमस्ते");\nString text = stringBox.getContent(); // बिना कास्टिंग के!\n\nBox<Integer> intBox = new Box<>();\nintBox.setContent(42);\nint number = intBox.getContent(); // बिना कास्टिंग के!\n\n// stringBox.setContent(123); // कंपाइलेशन त्रुटि!\n\n1.3. व्यवहार में लाभ
\n- \n
- प्रकार सुरक्षा: त्रुटियाँ runtime के बजाय कंपाइलेशन चरण में पकड़ी जाती हैं। \n
- स्पष्ट कास्टिंग का उन्मूलन: कोड साफ और अधिक पठनीय हो जाता है। \n
- कोड का पुन: उपयोग: एक क्लास/मेथड किसी भी प्रकार के साथ काम करता है। \n
2. व्यवहार में Generics: क्लासेस, मेथड्स और इंटरफेसेस
\n2.1. जेनेरिक क्लासेस
\nसबसे सामान्य मामला कलेक्शन है। लेकिन आप अपनी खुद की जेनेरिक क्लासेस बना सकते हैं:
\npublic class Pair<K, V> {\n private K key;\n private V value;\n \n public Pair(K key, V value) {\n this.key = key;\n this.value = value;\n }\n \n public K getKey() { return key; }\n public V getValue() { return value; }\n}\n\n// उपयोग\nPair<String, Integer> pair = new Pair<>("आयु", 30);\nString key = pair.getKey();\nInteger value = pair.getValue();\n\n2.2. जेनेरिक मेथड्स
\nएक मेथड के अपने स्वयं के प्रकार पैरामीटर हो सकते हैं, जो क्लास से संबंधित नहीं हैं:
\npublic class Utils {\n // ऐरे से लिस्ट बनाने के लिए जेनेरिक मेथड\n public static <T> List<T> arrayToList(T[] array) {\n List<T> list = new ArrayList<>();\n for (T element : array) {\n list.add(element);\n }\n return list;\n }\n}\n\n// उपयोग\nString[] names = {"अन्ना", "बोरिस", "विक्टर"};\nList<String> nameList = Utils.arrayToList(names);\n\n