परिचय: तारांकन और Any के साथ भ्रम क्यों सामान्य है
\n\nयदि आप कम से कम कुछ हफ्तों से Kotlin में कोड लिख रहे हैं, तो आपने निश्चित रूप से generics का सामना किया होगा। और यदि आपने मानक लाइब्रेरी के स्रोत कोड में झाँका है या List<*> जैसा कुछ लिखा है, तो हो सकता है कि आपने सोचा हो: "बस List<Any> क्यों नहीं लिख देते? आखिर तारांकन का मतलब 'कोई भी प्रकार' होता है ना?"
यह प्रश्न Stack Overflow पर एक वास्तविक हिट है (हजारों बार देखा गया)। और यह आश्चर्यजनक नहीं है: पहली नज़र में * और Any एक दूसरे के स्थान पर उपयोग किए जा सकने वाले लगते हैं। लेकिन वास्तव में उनके बीच एक बड़ा अंतर है, और यदि आप इसे नहीं पहचानते हैं, तो आप ऐसा कोड लिख सकते हैं जो कंपाइल नहीं होगा या, इससे भी बुरा, रनटाइम पर चुपचाप टूट जाएगा।
इस लेख में हम एक बार और हमेशा के लिए समझेंगे कि Kotlin generics में * (star projection) और Any में क्या अंतर है। आप जानेंगे कि उनमें से प्रत्येक का उपयोग कब करना है, वेरियन्स कैसे काम करता है, और तारांकन 'Any' क्यों नहीं है, बल्कि 'मुझे नहीं पता वहाँ क्या है, लेकिन मुझे परवाह नहीं है'। चलिए शुरू करते हैं!
Generics के बारे में संक्षेप में: उनकी आवश्यकता क्यों है
\n\nGenerics (सामान्यीकरण) हमें ऐसी क्लासेस और फंक्शन लिखने की अनुमति देते हैं जो विभिन्न प्रकारों के साथ काम करते हैं, बिना टाइप सुरक्षा खोए। Int के लिए अलग क्लास, String के लिए अलग और User के लिए अलग लिखने के बजाय, हम एक क्लास Box<T> लिखते हैं।
लेकिन क्या होगा यदि हम एक ऐसा फंक्शन लिखना चाहते हैं जो किसी भी Box को स्वीकार करता है, चाहे उसके अंदर कुछ भी हो? यहीं पर हमारे नायक प्रकट होते हैं: * और Any।
Any — एक विशिष्ट प्रकार है (लगभग Object की तरह)
\n\nKotlin में Any सभी non-null प्रकारों के लिए प्रकार पदानुक्रम का मूल है। यदि आप List<Any> लिखते हैं, तो इसका अर्थ है: "एक सूची जिसमें Any प्रकार के तत्व हैं"। यानी आप इसमें Int, String, User — कुछ भी डाल सकते हैं, क्योंकि वे सभी Any से विरासत में मिलते हैं।
महत्वपूर्ण बिंदु: List<Any> एक विशिष्ट पैरामीटरयुक्त प्रकार है। कंपाइलर जानता है कि तत्व का प्रकार Any है।
val listOfAny: MutableList<Any> = mutableListOf(1, "hello", 3.14)\nlistOfAny.add(42) // OK\nlistOfAny.add("world") // OK\n\n\nयहाँ सब कुछ अनुमानित है: हमने स्पष्ट रूप से कहा कि सूची Any के साथ काम करती है। लेकिन एक बारीक बात है: MutableList<Any>, MutableList<String> नहीं है। आप स्ट्रिंग्स की सूची को वहाँ पास नहीं कर सकते जहाँ Any सूची की अपेक्षा की जाती है (जब तक कि आप कोवेरियन्स का उपयोग नहीं करते, लेकिन इसके बारे में बाद में)।
तारांकन (*) — 'मुझे नहीं पता, लेकिन मुझे परवाह नहीं है'
\n\nतारांकन, या star projection, पूरी तरह से एक अलग कहानी है। जब आप List<*> लिखते हैं, तो आप कंपाइलर से कह रहे हैं: "यह किसी प्रकार की सूची है, लेकिन मुझे नहीं पता कि वास्तव में किस प्रकार की, और मुझे इससे कोई फर्क नहीं पड़ता"। यह अज्ञात प्रकार का प्रक्षेपण है।
मुख्य अंतर: * कोई प्रकार नहीं है, बल्कि एक wildcard (स्थानापन्न चिह्न) है। इसका अर्थ है कि प्रकार अज्ञात है, और कंपाइलर इस बात पर प्रतिबंध लगाता है कि आप ऐसी वस्तु के साथ क्या कर सकते हैं।
fun printLength(list: List<*>) {\n // list.add(1) // कंपाइलेशन त्रुटि! तत्व नहीं जोड़ सकते\n println(list.size) // OK: आकार पता कर सकते हैं\n for (item in list) {\n println(item) // OK: item का प्रकार Any? है\n }\n}\n\n\nध्यान दें: जब आप List<*> से तत्व पढ़ते हैं, तो उनका प्रकार Any? होता है (क्योंकि सूची में null हो सकता है)। लेकिन आप तत्वों को जोड़ नहीं सकते, क्योंकि कंपाइलर नहीं जानता कि वहाँ वास्तव में कौन सा प्रकार संग्रहीत है। यह टाइप सुरक्षा के उल्लंघन से सुरक्षा है।
दो शब्दों में मुख्य अंतर
\n\n- \n
List<Any>— यह एक सूची है जिसमें शामिल हो सकते हैं