परिचय: स्मार्ट डेवलपर्स भी Kotlin में क्यों फिसलते हैं
\n\nKotlin एक सुरुचिपूर्ण और शक्तिशाली भाषा है जिसने लाखों डेवलपर्स का दिल जीता है। लेकिन इसकी संक्षिप्तता और लचीलापन, विशेष रूप से Java या अन्य भाषाओं से आने वाले शुरुआती लोगों के लिए, जाल छिपाए हुए हैं। कई लोग सोचते हैं कि चूंकि Kotlin Java से "अधिक समझने योग्य" है, तो गलतियाँ भी कम होंगी। व्यवहार में, इसका उल्टा होता है: भाषा के दर्शन को न समझना ऐसी बग्स को जन्म देता है जिन्हें पकड़ना मुश्किल होता है।
\n\nइस लेख में, हम शुरुआती Kotlin डेवलपर्स की शीर्ष गलतियों पर चर्चा करेंगे - सामान्य null safety को अनदेखा करने से लेकर coroutines के गलत उपयोग तक। प्रत्येक गलती को कोड उदाहरण और इसे टालने के व्यावहारिक सुझाव के साथ समझाया जाएगा।
1. Null Safety: जब "?" बचाता है, और जब नहीं
\n\n1.1. Nullable प्रकारों को अनदेखा करना
\nसबसे आम गलती है !! (not-null assertion) का उपयोग वहाँ करना जहाँ सुरक्षित कॉल से काम चल सकता है। शुरुआती अक्सर लिखते हैं:
fun printLength(str: String?) {\n println(str!!.length) // यदि str == null है तो NPE फेंकेगा\n}\n\nसही तरीका है ?.let या एल्विस ऑपरेटर ?: का उपयोग करना:
fun printLength(str: String?) {\n str?.let { println(it.length) } ?: println("स्ट्रिंग null है")\n}\n\n1.2. Smart Cast के बारे में भूल जाना
\nKotlin null की जाँच के बाद स्वचालित रूप से प्रकारों को परिवर्तित करता है, लेकिन शुरुआती इसे नोटिस नहीं करते:
\n\nfun process(value: Any?) {\n if (value is String) {\n // value पहले से ही String है, कास्ट करने की आवश्यकता नहीं!\n println(value.length) // (value as String).length के बिना काम करता है\n }\n}\n\nसुझाव: कंपाइलर पर भरोसा करें। !! का उपयोग केवल तभी करें जब आप 100% आश्वस्त हों कि मान null नहीं है, और गलती होने पर क्रैश के लिए तैयार हों।
2. Sealed Class और When: संपूर्णता के छिपे खतरे
\n\n2.1. Sealed class के साथ else-शाखा का उपयोग
\nSealed class इसलिए बनाए गए हैं ताकि कंपाइलर जाँच करे कि सभी विकल्पों को संभाला गया है या नहीं। लेकिन शुरुआती अक्सर else जोड़ देते हैं, जिससे सारा जादू खत्म हो जाता है:
sealed class Result {\n data class Success(val data: String) : Result()\n data class Error(val message: String) : Result()\n}\n\nfun handleResult(result: Result) {\n when (result) {\n is Result.Success -> println(result.data)\n is Result.Error -> println(result.message)\n else -> {} // गलती! यदि कोई नया उपवर्ग जोड़ा जाता है, तो कंपाइलर चेतावनी नहीं देगा\n }\n}\n\nसही तरीका है else का उपयोग न करना:
\n\nfun handleResult(result: Result) {\n when (result) {\n is Result.Success -> println(result.data)\n is Result.Error -> println(result.message)\n } // यदि सभी शाखाएँ कवर नहीं की गईं तो कंपाइलर त्रुटि देगा\n}\n\n2.2. Sealed class और enum के बीच भ्रम
\nशुरुआती अक्सर वहाँ enum चुन लेते हैं जहाँ sealed class की आवश्यकता होती है। याद रखें: enum स्थिरांकों के एक सीमित सेट के लिए है, sealed class प्रत्येक उपवर्ग में अलग-अलग डेटा वाले पदानुक्रम के लिए है।
3. Scope Functions: apply, let, run, with, also — कैसे भ्रमित न हों
\n\n3.1. संदर्भ का मिश्रण: this बनाम it
\nसबसे आम भ्रम तब होता है जब शुरुआती उन कार्यों के लिए apply (संदर्भ — this) का उपयोग करते हैं जहाँ let (संदर्भ — it) की आवश्यकता होती है:
data class User(var name: String, var age: Int)\n\nfun initUser(): User {\n return User("अनाम", 0).apply {\n // this — User है\n name = "इवान"\n age = 25\n }\n}\n\nfun printUserInfo(user: User?) {\n user?.let { \n // it — user है\n println("नाम: ${it.name}, आयु: ${it.age}")\n }\n}\n\n3.2. बिना अर्थ के Scope functions की श्रृंखला
\nशुरुआती इस तरह की श्रृंखलाएँ लिखना पसंद करते हैं:
\n\nlist?.let { it.filter { it > 5 }.also { println(it) } }