Kotlin में out शब्द के पीछे क्या छिपा है? लोकप्रिय प्रश्नों के उत्तर
\n\nयदि आपने कभी Kotlin में जेनेरिक लिखे हैं और एक समझ से परे कंपाइलेशन त्रुटि का सामना किया है, तो संभवतः आपने out शब्द देखा होगा। यह छोटा सा मॉडिफायर अनुभवी डेवलपर्स के बीच भी बहुत विवाद का कारण बनता है। हमने Stack Overflow से सबसे सामान्य प्रश्न एकत्र किए हैं और उन्हें एक समझने योग्य मार्गदर्शिका में बदल दिया है। आज आप जानेंगे कि out क्या है, इसकी आवश्यकता क्यों है, और कोवेरिएंस में कैसे भ्रमित न हों।
आइए इसे हमेशा के लिए समझ लें — बिना उबाऊ सिद्धांत के, केवल वास्तविक कोड और अभ्यास के साथ।
\n\nKotlin ने out क्यों बनाया? (इतिहास में एक संक्षिप्त भ्रमण)
\n\nJava में wildcard है — प्रश्न चिह्न ? extends T। इसका मतलब है कि कलेक्शन में T प्रकार या उसके उपप्रकारों की वस्तुएँ हो सकती हैं। समस्या यह है कि Java आपको गलती से ऐसे कलेक्शन में गलत वस्तु डालने से नहीं रोकता — आपको त्रुटि केवल रनटाइम पर मिलेगी। Kotlin ने इस समस्या को टाइप स्तर पर हल किया।
Kotlin में कीवर्ड out कोवेरिएंस की घोषणा है। यदि आप Source<out T> लिखते हैं, तो इसका अर्थ है: "मैं इस Source से T प्रकार की वस्तुएँ पढ़ सकता हूँ, लेकिन इसमें लिख नहीं सकता"। कंपाइलर इस पर सख्ती से नज़र रखता है।
प्रश्न 1: out, in से कैसे अलग है?
\n\nStack Overflow पर सबसे सामान्य प्रश्न। आइए एक बक्से की कल्पना करें:
\n\n- \n
out— एक बक्सा जिससे आप केवल चीज़ें निकाल सकते हैं (उत्पादन करता है)। \n in— एक बक्सा जिसमें आप केवल चीज़ें रख सकते हैं (उपभोग करता है)। \n
उदाहरण:
\n\n// कोवेरिएंस: पढ़ सकते हैं, लेकिन लिख नहीं सकते\ninterface Producer<out T> {\n fun produce(): T\n}\n\n// कॉन्ट्रावेरिएंस: लिख सकते हैं, लेकिन पढ़ नहीं सकते\ninterface Consumer<in T> {\n fun consume(item: T)\n}\n\nfun main() {\n val stringProducer: Producer<String> = object : Producer<String> {\n override fun produce(): String = "Hello"\n }\n \n val anyProducer: Producer<Any> = stringProducer // काम करता है! out अनुमति देता है\n println(anyProducer.produce())\n}\n\n\nध्यान दें: Producer<out T> आपको Producer<String> को Producer<Any> प्रकार के वेरिएबल में असाइन करने की अनुमति देता है। out के बिना यह असंभव होता।
प्रश्न 2: Kotlin out के साथ fun set(value: T) को क्यों प्रतिबंधित करता है?
\n\nयह एक क्लासिक जाल है। यदि आपने out घोषित किया है, तो कंपाइलर उन फंक्शन्स की अनुमति नहीं देगा जो T को पैरामीटर के रूप में लेते हैं। क्यों?
कल्पना करें कि आपके पास MutableList<out Number> है। आप इसमें Int डाल सकते हैं, लेकिन कंपाइलर नहीं जानता कि यह वास्तव में कौन सी सूची है। शायद यह MutableList<Double> है? यदि लिखने की अनुमति दी जाती, तो आप टाइप तोड़ देते। out सुरक्षा सुनिश्चित करता है: आप केवल पढ़ सकते हैं।
// कंपाइल नहीं होगा!\ninterface BadList<out T> {\n fun get(index: Int): T // OK\n fun add(item: T) // त्रुटि! T in-स्थिति में है\n}\n\n\nसमाधान: यदि आपको पढ़ना और लिखना दोनों चाहिए — तो in और out का अलग-अलग उपयोग करें, या इनवेरिएंट प्रकार (बिना मॉडिफायर के) का उपयोग करें।
\n\nप्रश्न 3: out Java के wildcard ? extends से कैसे संबंधित है?
\n\nसीधा संबंध: Kotlin में Source<out T> = Java में Source<? extends T>। लेकिन एक महत्वपूर्ण अंतर है:
- \n
- Java में wildcard टाइप का उपयोग है (use-site variance)। \n
- Kotlin में
outटाइप की घोषणा है (declaration-site variance)। \n
Java में उदाहरण:
\n\n// Java: वेरिएबल घोषित करते समय wildcard का उपयोग\nList<? extends Number> numbers = new ArrayList<Integer>();\nnumbers.a