Kotlin में Generics: क्यों ज़रूरी हैं और कैसे उपयोग करें
Kotlin में विकास करते समय, आप अक्सर ऐसा कोड लिखने की आवश्यकता का सामना करते हैं जो विभिन्न डेटा प्रकारों के साथ काम कर सके। Int, String, Double आदि के लिए अलग-अलग फ़ंक्शन या क्लास बनाने के बजाय, आप Generics (सामान्यीकरण) तंत्र का उपयोग कर सकते हैं। यह लेख बताएगा कि Kotlin में Generics क्या हैं, वे क्यों ज़रूरी हैं और व्यवहार में उनका उपयोग कैसे करें।
Generics क्या हैं और वे क्यों ज़रूरी हैं?
Generics (सामान्यीकरण) एक उपकरण है जो आपको ऐसी क्लास, इंटरफ़ेस और फ़ंक्शन लिखने की अनुमति देता है जो मनमाने डेटा प्रकारों के साथ काम करते हैं। कल्पना करें कि आप एक बॉक्स बना रहे हैं जिसमें आप कुछ भी रख सकते हैं: एक किताब, फ़ोन या खाना। प्रत्येक वस्तु के लिए अलग बॉक्स बनाने के बजाय, आप एक सार्वभौमिक बॉक्स बनाते हैं।
Generics के उपयोग के मुख्य लाभ:
- प्रकार सुरक्षा (Type Safety) — कंपाइलर संकलन चरण में प्रकारों की जाँच करता है, जो रनटाइम त्रुटियों को रोकता है।
- कोड का पुन: उपयोग — एक बार लिखा गया कोड किसी भी प्रकार के साथ काम करता है।
- पठनीयता — कोड अधिक समझने योग्य हो जाता है, क्योंकि यह स्पष्ट रूप से बताता है कि वह किन प्रकारों के साथ काम करता है।
Generics के बिना, आपको Any का उपयोग करना होगा और लगातार प्रकारों को परिवर्तित करना होगा, जो त्रुटियों से भरा होता है। Generics के साथ, कंपाइलर स्वयं प्रकारों की शुद्धता पर नज़र रखता है।
Kotlin में Generics की मूल बातें: प्रकार पैरामीटर
Kotlin में, Generics को प्रकार पैरामीटर के माध्यम से कार्यान्वित किया जाता है। उन्हें कोण कोष्ठक <> में निर्दिष्ट किया जाता है। अक्सर अक्षरों T (Type), E (Element), K (Key), V (Value) का उपयोग किया जाता है, लेकिन आप किसी भी नाम का उपयोग कर सकते हैं।
Generic-फ़ंक्शन
चलिए एक सरल उदाहरण से शुरू करते हैं — एक फ़ंक्शन जो दिए गए तर्क को वापस करता है:
fun <T> identity(value: T): T { return value}
fun main() { val number: Int = identity(42) val text: String = identity("नमस्ते") println(number) // 42 println(text) // नमस्ते}यहाँ फ़ंक्शन नाम से पहले <T> प्रकार पैरामीटर घोषित करता है। प्रकार T तर्क से स्वचालित रूप से निकाला जाता है, इसलिए इसे स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता नहीं है।
Generic-क्लास
क्लास भी सामान्यीकृत हो सकती हैं। आइए एक सरल कंटेनर बनाएं:
class Box<T>(private val item: T) { fun getItem(): T { return item }}
fun main() { val stringBox = Box("किताब") val intBox = Box(123)
println(stringBox.getItem()) // किताब println(intBox.getItem()) // 123}क्लास Box<T> किसी भी प्रकार की वस्तु को संग्रहीत कर सकती है। इंस्टेंस बनाते समय, हम एक विशिष्ट प्रकार निर्दिष्ट करते हैं, और कंपाइलर गारंटी देता है कि getItem() बिल्कुल वही प्रकार लौटाएगा।
प्रकार सीमाएँ (Type Bounds)
कभी-कभी उन प्रकारों को सीमित करने की आवश्यकता होती है जिनका उपयोग Generic में किया जा सकता है। उदाहरण के लिए, ताकि फ़ंक्शन केवल संख्याओं के साथ काम करे। इसके लिए कुंजी शब्द : के साथ सीमाएँ (bounds) का उपयोग किया जाता है।
ऊपरी सीमा (Upper Bound)
fun <T : Number> sum(a: T, b: T): Double { return a.toDouble() + b.toDouble()}
fun main() { println(sum(10, 20)) // 30.0 println(sum(3.5, 2.5)) // 6.0 // println(sum("A", "B")) // संकलन त्रुटि}यहाँ T : Number का अर्थ है कि प्रकार T को Number क्लास का वंशज होना चाहिए। Int, Double, Float उपयुक्त हैं, लेकिन String नहीं।
एकाधिक सीमाएँ
यदि कई सीमाओं की आवश्यकता है, तो where का उपयोग करें:
interface Printable { fun print()}
fun <T>