Swift में Generics क्या हैं और वे क्यों ज़रूरी हैं
\n\nकल्पना करें कि आप एक फ़ंक्शन लिख रहे हैं जो दो संख्याओं को आपस में बदलता है। फिर आपको स्ट्रिंग के लिए भी ऐसा ही फ़ंक्शन चाहिए। और फिर तारीखों के लिए। यदि Generics (सामान्यीकरण) नहीं होते, तो आपको तीन समान फ़ंक्शन लिखने पड़ते, जो केवल डेटा प्रकारों में भिन्न होते। यह DRY (Don't Repeat Yourself) सिद्धांत का उल्लंघन करता है और कोड को बोझिल बनाता है।
\n\nGenerics (जेनेरिक्स या सामान्यीकृत प्रकार) Swift में एक शक्तिशाली तंत्र है जो आपको लचीला, पुन: प्रयोज्य कोड लिखने की अनुमति देता है जो किसी भी डेटा प्रकार के साथ काम करता है। एक विशिष्ट प्रकार निर्दिष्ट करने के बजाय, आप एक placeholder (प्लेसहोल्डर) का उपयोग करते हैं, जो फ़ंक्शन को कॉल करने या संरचना बनाने पर वास्तविक प्रकार से बदल दिया जाता है।
\n\nइस लेख में, हम समझेंगे कि Swift में Generics कैसे काम करते हैं, और व्यावहारिक उदाहरणों के साथ दिखाएंगे कि iOS विकास में उनका उपयोग कैसे करें।
\n\n1. Generics की मूल बातें: फ़ंक्शन और प्रकार
\n\nआइए एक सरल उदाहरण से शुरू करते हैं। एक फ़ंक्शन लिखते हैं जो दो मानों को आपस में बदलता है:
\n\n// Generics के बिना — प्रत्येक प्रकार के लिए अलग लिखना होगा\nfunc swapInts(_ a: inout Int, _ b: inout Int) {\n let temp = a\n a = b\n b = temp\n}\n\nfunc swapStrings(_ a: inout String, _ b: inout String) {\n let temp = a\n a = b\n b = temp\n}\n\n// Generics के साथ — सभी प्रकारों के लिए एक फ़ंक्शन\nfunc swapValues<T>(_ a: inout T, _ b: inout T) {\n let temp = a\n a = b\n b = temp\n}\n\nvar x = 10, y = 20\nswapValues(&x, &y) // Int के साथ काम करता है\nprint("x: \\(x), y: \\(y)") // x: 20, y: 10\n\nvar str1 = "Hello", str2 = "World"\nswapValues(&str1, &str2) // String के साथ काम करता है\nprint("str1: \\(str1), str2: \\(str2)") // str1: World, str2: Hello\n\nयहाँ <T> एक सार्वभौमिक प्रकार पैरामीटर है। अक्षर T सिर्फ एक परंपरा है (Type शब्द से), आप कोई भी नाम उपयोग कर सकते हैं: <Element>, <ValueType> आदि।
Generics केवल फ़ंक्शन के साथ ही नहीं, बल्कि प्रकारों के साथ भी काम करते हैं। एक सरल कंटेनर संरचना पर विचार करें:
\n\n// एक मान संग्रहीत करने के लिए सामान्यीकृत संरचना\nstruct Box<T> {\n let value: T\n \n init(_ value: T) {\n self.value = value\n }\n}\n\nlet intBox = Box(42) // प्रकार Box<Int>\nlet stringBox = Box("Swift") // प्रकार Box<String>\nlet arrayBox = Box([1, 2, 3]) // प्रकार Box<[Int]>\n\nprint(intBox.value) // 42\nprint(stringBox.value) // Swift\n\nSwift कंपाइलर पारित मान के आधार पर स्वचालित रूप से T का प्रकार निर्धारित करता है। इसे type inference (प्रकार अनुमान) कहा जाता है।
2. प्रकार प्रतिबंध (Type Constraints)
\n\nकभी-कभी आपको यह सीमित करने की आवश्यकता होती है कि आपके सामान्यीकरण के साथ किन प्रकारों का उपयोग किया जा सकता है। उदाहरण के लिए, आप एक फ़ंक्शन लिखना चाहते हैं जो एक सरणी में अधिकतम मान ढूंढता है। लेकिन ऑपरेटर > सभी प्रकारों के साथ काम नहीं करता — केवल उन प्रकारों के साथ जो Comparable प्रोटोकॉल का अनुपालन (conform) करते हैं।
// प्रतिबंध: T को Comparable प्रोटोकॉल का अनुपालन करना चाहिए\nfunc findMax<T: Comparable>(in array: [T]) -> T? {\n guard !array.isEmpty else { return nil }\n \n var maxValue = array[0]\n for element in array {\n if element > maxValue {\n maxValue = element\n }\n }\n return maxValue\n}\n\nlet numbers = [3, 7, 1, 9, 5]\nprint(findMax(in: numbers) ?? "No max") // 9\n\nlet words = ["apple", "zebra", "banana"]\nprint(findMax(in: words) ?? "No max") // "zebra" (वर्णमाला क्रम में)\n\nसिंटैक्स <T: Comparable> का अर्थ है: "प्रकार T को Comparable प्रोटोकॉल का अनुपालन करना चाहिए"। आप अल्पविराम से अलग करके कई प्रतिबंध जोड़ सकते हैं या अधिक जटिल शर्तों के लिए where का उपयोग कर सकते हैं:
// where के माध्यम से कई प्रतिबंध\nfunc process<T>(_ value: T) where T: Numeric, T: Comparable {\n if value > 0 {\n print("सकारात्मक