प्रोग्रामर 5.2 के लिए Kotlin बूटकैंप: जेनेरिक

यह कोडलैब प्रोग्रामर कोर्स के लिए Kotlin बूटकैंप का हिस्सा है. अगर आप कोडलैब के क्रम में काम करते हैं, तो आपको इस कोर्स का ज़्यादा से ज़्यादा फ़ायदा मिलेगा. अपनी जानकारी के आधार पर, आप कुछ सेक्शन को हाइलाइट कर सकते हैं. यह कोर्स उन प्रोग्रामर को ध्यान में रखकर बनाया गया है जो ऑब्जेक्ट पर आधारित भाषा जानते हैं और कोटलिन सीखना चाहते हैं.

परिचय

इस कोडलैब में आपको सामान्य क्लास, फ़ंक्शन, और तरीकों के बारे में बताया गया है. साथ ही, यह भी बताया गया है कि वे Kotlin में कैसे काम करते हैं.

सिर्फ़ एक नमूने वाला ऐप्लिकेशन बनाने के बजाय, इस कोर्स के पाठ आपकी जानकारी देने के लिए डिज़ाइन किए गए हैं. हालांकि, आप एक-दूसरे से खुद को अलग नहीं कर सकते हैं, ताकि आप उन सेक्शन को हाइलाइट कर सकें जिनसे आप सहमत हैं. इन्हें एक साथ जोड़ने के लिए, कई उदाहरणों में एक्वेरियम की थीम का इस्तेमाल किया गया है. अगर आप पूरी अक्वेरियम की कहानी देखना चाहते हैं, तो प्रोग्रामर के लिए Kotlin बूटकैंप Udacity कोर्स देखें.

आपको क्या पता होना चाहिए

  • Kotlin के फ़ंक्शन, क्लास, और मेथड का सिंटैक्स
  • IntelliJ IDEA में नई क्लास बनाने और कोई प्रोग्राम चलाने का तरीका

आप इन चीज़ों के बारे में जानेंगे

  • जेनरिक क्लास, तरीकों, और फ़ंक्शन के साथ काम करने का तरीका

आप क्या कर पाएंगे!

  • जेनरिक क्लास बनाना और कंस्ट्रेंट जोड़ना
  • in और out टाइप बनाएं
  • जेनरिक फ़ंक्शन, मेथड, और एक्सटेंशन फ़ंक्शन बनाएं

जेनरिक के बारे में जानकारी

Kotlin में कई तरह की प्रोग्रामिंग भाषाएं होती हैं. जेनरिक टाइप की मदद से, आप क्लास को सामान्य बना सकते हैं. इससे क्लास को ज़्यादा सुविधाजनक बनाया जा सकता है.

मान लें कि आप एक MyList क्लास लागू कर रहे थे जिसमें आइटम की सूची है. जेनरिक के बिना, आपको हर तरह के MyList के लिए एक नया वर्शन लागू करना होगा: Double के लिए एक, String के लिए एक, Fish के लिए एक. जेनरिक के साथ, आप सूची को जेनरिक बना सकते हैं, ताकि वह किसी भी तरह का ऑब्जेक्ट होल्ड कर सके. यह एक तरह का वाइल्डकार्ड बनाता है, जो कई तरह के फ़िट हो सकता है.

जेनरिक टाइप को परिभाषित करने के लिए, क्लास के नाम के बाद T ऐंगल ब्रैकेट <T> का इस्तेमाल करें. (आप किसी दूसरे अक्षर या लंबे नाम का इस्तेमाल कर सकते हैं, लेकिन जेनरिक टाइप की परंपरा T है.)

class MyList<T> {
    fun get(pos: Int): T {
        TODO("implement")
    }
    fun addItem(item: T) {}
}

आप T का इस्तेमाल किसी सामान्य टाइप की तरह कर सकते हैं. get() का रिटर्न टाइप T है और addItem() का पैरामीटर T है. हालांकि, सामान्य सूचियां बहुत काम की होती हैं, इसलिए Kotlin में List क्लास पहले से मौजूद होती है.

पहला चरण: टाइप हैरारकी बनाना

इस कदम में आप अगले चरण में इस्तेमाल करने के लिए कुछ क्लास बनाते हैं. सब-क्लासिंग को पहले के कोडलैब में शामिल किया गया था. हालांकि, यहां कम शब्दों में समीक्षा की गई है.

  1. उदाहरण को व्यवस्थित रखने के लिए, src में एक नया पैकेज बनाएं और इसे generics नाम दें.
  2. सामान्य पैकेज में, एक नई Aquarium.kt फ़ाइल बनाएं. इससे आप बिना किसी टकराव के एक जैसे नाम का इस्तेमाल करके चीज़ों को फिर से तय कर सकते हैं, इसलिए इस कोडलैब के लिए आपका बाकी कोड इस फ़ाइल में चला जाता है.
  3. पानी की सप्लाई के प्रकारों को क्रम से लगाना. WaterSupply को open क्लास बनाकर शुरू करें, ताकि उन्हें सब-क्लास किया जा सके.
  4. बूलियन var पैरामीटर, needsProcessing जोड़ें. इससे गैटर और सेटर के साथ अपने-आप एक बदली जा सकने वाली प्रॉपर्टी बन जाती है.
  5. एक सब-क्लास TapWater बनाएं, जो WaterSupply तक बढ़ती है. साथ ही, true को needsProcessing के लिए पास करें, क्योंकि टैप वॉटर में ऐसी चीज़ें होती हैं जो मछली के लिए खराब होती हैं.
  6. TapWater में, addChemicalCleaners() नाम के फ़ंक्शन के बारे में बताएं, जो पानी को साफ़ करने के बाद needsProcessing को false पर सेट करता है. needsProcessing प्रॉपर्टी को TapWater से सेट किया जा सकता है, क्योंकि यह डिफ़ॉल्ट रूप से public होती है और सब-क्लास से ऐक्सेस की जा सकती है. यह रहा पूरा कोड.
package generics

open class WaterSupply(var needsProcessing: Boolean)

class TapWater : WaterSupply(true) {
   fun addChemicalCleaners() {
       needsProcessing = false
   }
}
  1. WaterSupply के दो और सब-क्लास बनाएं, जिन्हें FishStoreWater और LakeWater कहा जाता है. FishStoreWater को प्रोसेस करने की ज़रूरत नहीं है, लेकिन LakeWater को filter() तरीके का इस्तेमाल करके फ़िल्टर करना ज़रूरी है. फ़िल्टर करने के बाद, इसे फिर से प्रोसेस करने की ज़रूरत नहीं होती. इसलिए, filter() में needsProcessing = false सेट करें.
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
   fun filter() {
       needsProcessing = false
   }
}

अगर आपको ज़्यादा जानकारी चाहिए, तो Kotlin में इनहेरिट करने के बारे में पहले दिए गए लेसन को देखें.

दूसरा चरण: जेनरिक क्लास बनाना

इस तरीके से आप Aquarium क्लास में बदलाव करके अलग-अलग तरह के वॉटर सप्लाई के लिए काम कर पाएंगे.

  1. Aquarium.kt में Aquarium को परिभाषित करें. इसके बाद, क्लास के नाम के बाद ब्रैकेट में <T> लगाएं.
  2. Aquarium में T टाइप की waterSupply नहीं बदली जा सकने वाली प्रॉपर्टी जोड़ें.
class Aquarium<T>(val waterSupply: T)
  1. genericsExample() नाम का फ़ंक्शन लिखें. यह #39; क्लास का हिस्सा नहीं है, इसलिए यह फ़ाइल के टॉप लेवल पर जा सकता है, जैसे कि main() फ़ंक्शन या क्लास की परिभाषाएं. फ़ंक्शन में, Aquarium बनाएं और उसे WaterSupply पास करें. waterSupply पैरामीटर जेनरिक है, इसलिए आपको ऐंगल ब्रैकेट <> में टाइप तय करना होगा.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. genericsExample() में आपका कोड, अक्वेरियम और #39 की waterSupply को ऐक्सेस कर सकता है. यह TapWater टाइप का है, इसलिए आप किसी भी तरह के कास्ट के बिना addChemicalCleaners() को कॉल कर सकते हैं.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Aquarium ऑब्जेक्ट बनाते समय, आप ऐंगल ब्रैकेट और उनके बीच के #&336 को हटा सकते हैं, क्योंकि Kotlin में टाइप का अनुमान होता है. इसलिए, इंस्टेंस बनाते समय TapWater को दो बार बोलने की ज़रूरत नहीं है. यह टाइप, Aquarium के तर्क के हिसाब से लगाया जा सकता है. इससे अब भी TapWater टाइप का Aquarium बन जाएगा.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. यह देखने के लिए कि क्या हो रहा है, needsProcessing को addChemicalCleaners() पर कॉल करने से पहले और बाद में प्रिंट करें. नीचे पूरा किया गया फ़ंक्शन है.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
    aquarium.waterSupply.addChemicalCleaners()
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}
  1. genericsExample() को कॉल करने के लिए main() फ़ंक्शन जोड़ें. इसके बाद, अपना प्रोग्राम चलाएं और नतीजे की निगरानी करें.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

तीसरा चरण: प्रॉडक्ट की सेटिंग को ज़्यादा सटीक बनाना

जेनेरिक का मतलब है कि आप करीब-करीब सब कुछ पास कर सकते हैं, और कभी-कभी कोई समस्या हो सकती है. इस चरण में आप यह तय कर सकते हैं कि Aquarium क्लास में क्या डाला जा सकता है.

  1. genericsExample() में, waterSupply के लिए स्ट्रिंग पास करने वाला Aquarium बनाएं. इसके बाद, एक्वेरियम और #39; की waterSupply प्रॉपर्टी प्रिंट करें.
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. प्रोग्राम को चलाकर देखें.
⇒ string

नतीजे के तौर पर, पास की गई स्ट्रिंग मान्य होती है. ऐसा इसलिए, क्योंकि AquariumT. पर कोई पाबंदी नहीं लगाता है. इसमें String भी शामिल है.

  1. genericsExample() में waterSupply के लिए null पास करने वाला एक और Aquarium बनाएं. अगर waterSupply शून्य है, तो "waterSupply is null" का प्रिंट लें.
fun genericsExample() {
    val aquarium3 = Aquarium(null)
    if (aquarium3.waterSupply == null) {
        println("waterSupply is null")
    }
}
  1. प्रोग्राम चलाएं और नतीजों पर ध्यान दें.
⇒ waterSupply is null

Aquarium बनाते समय आप null को क्यों पास कर सकते हैं? ऐसा इसलिए होता है, क्योंकि T डिफ़ॉल्ट रूप से शून्य होने वाले Any? टाइप का होता है, जो टाइप हैरारकी के ऊपर होता है. यह आपके टाइप किए गए शब्दों के बराबर है.

class Aquarium<T: Any?>(val waterSupply: T)
  1. अगर आप null को पास करने की अनुमति नहीं देना चाहते, तो Any के बाद ? को हटाकर, T टाइप का Any साफ़ तौर पर बताएं.
class Aquarium<T: Any>(val waterSupply: T)

इस कॉन्टेक्स्ट में, Any को सामान्य कंस्ट्रेंट कहा जाता है. इसका मतलब है कि T के लिए कोई भी टाइप पास किया जा सकता है. ज़रूरी है कि यह null हो.

  1. आपको यह पक्का करना है कि T के लिए सिर्फ़ WaterSupply (या इसके सब-क्लास में से कोई एक) पास किया जाना चाहिए. ज़्यादा खास सामान्य कंस्ट्रेंट के बारे में बताने के लिए, Any को WaterSupply से बदलें.
class Aquarium<T: WaterSupply>(val waterSupply: T)

चौथा चरण: ज़्यादा जांच जोड़ना

इस कदम में, आपको check() फ़ंक्शन के बारे में पता चलता है. इससे यह पक्का करने में मदद मिलती है कि आपका कोड उम्मीद के मुताबिक काम कर रहा है या नहीं. check()फ़ंक्शन, Kotlin में एक स्टैंडर्ड लाइब्रेरी फ़ंक्शन है. यह दावे के तौर पर काम करता है. अगर इसके आर्ग्युमेंट का आकलन false पर होता है, तो यह IllegalStateException का इस्तेमाल करेगा.

  1. check() जोड़ने के साथ, Aquarium की क्लास में addWater() जोड़ने का तरीका जोड़ें. इससे यह पक्का हो जाएगा कि आपको पहले पानी प्रोसेस करने की ज़रूरत नहीं है.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
    fun addWater() {
        check(!waterSupply.needsProcessing) { "water supply needs processing first" }
        println("adding water from $waterSupply")
    }    
}

इस मामले में, अगर needsProcessing सही है, तो check() एक अपवाद रिटर्न करेगा.

  1. genericsExample() में, कोड जोड़कर LakeWater के साथ Aquarium बनाएं, फिर उसमें पानी डालें.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.addWater()
}
  1. अपना प्रोग्राम चलाएं और आपको एक अपवाद मिलेगा, क्योंकि पहले पानी को फ़िल्टर करना होगा.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
        at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
  1. Aquarium को पानी में जोड़ने से पहले, फ़िल्टर करने के लिए एक कॉल जोड़ें. प्रोग्राम चलाने पर, अब कोई अपवाद नहीं दिखेगा.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.waterSupply.filter()
    aquarium4.addWater()
}
⇒ adding water from generics.LakeWater@880ec60

ऊपर दी गई बुनियादी जानकारी में बुनियादी बातें बताई गई हैं. इन टास्क में और भी चीज़ें शामिल हैं, लेकिन ज़रूरी है कि आप जेनरिक कंस्ट्रेंट के साथ जेनरिक क्लास का एलान करें और उसका इस्तेमाल करें.

इस टास्क में, आप जेनरिक विज्ञापनों के साथ इन और आउट टाइप के बारे में जान सकते हैं. in टाइप टाइप को सिर्फ़ क्लास में पास किया जा सकता है, रिटर्न नहीं. out एक ऐसा टाइप है जिसे सिर्फ़ क्लास से लौटाया जा सकता है.

Aquarium क्लास पर नज़र डालें और आप देख पाएंगे कि #39; जेनरिक टाइप सिर्फ़ waterSupply प्रॉपर्टी पाने पर ही दिखेगा. कोई भी ऐसा तरीका नहीं है जो T टाइप की वैल्यू को पैरामीटर के तौर पर लेता हो (कंस्ट्रक्टर में इसे परिभाषित करने के अलावा). Kotlin की मदद से, इस केस के लिए out टाइप को तय किया जा सकता है. साथ ही, इससे यह भी पता लगाया जा सकता है कि टाइप कहां इस्तेमाल किए जा सकते हैं. इसी तरह, आप सामान्य टाइप के लिए in टाइप तय करें, जो सिर्फ़ मेथड में पास किए जाते हैं, न कि रिटर्न किए गए. इससे Kotlin को कोड की सुरक्षा के लिए ज़्यादा जांच करने की अनुमति मिल जाती है.

in और out टाइप, Kotlin's टाइप सिस्टम के लिए निर्देश हैं. पूरे टाइप के सिस्टम को समझाना इस बूटकैंप के दायरे से बाहर है(&&30; बहुत शामिल है); हालांकि, कंपाइलर ऐसे टाइप को फ़्लैग करेगा जिन्हें in और out सही तरीके से मार्क न किया गया हो, इसलिए आपको उनके बारे में जानना चाहिए.

पहला चरण: आउट टाइप के बारे में बताना

  1. Aquarium क्लास में, T: WaterSupply को out टाइप में बदलें.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. उसी फ़ाइल में, क्लास के बाहर, addItemTo() फ़ंक्शन का एलान करें, जो WaterSupply के Aquarium की उम्मीद करता है.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. genericsExample() से addItemTo() को कॉल करें और अपना प्रोग्राम चलाएं.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

Kotlin में addItemTo() यह पक्का कर सकता है कि आम तौर पर इस्तेमाल होने वाली WaterSupply नीति का उल्लंघन करने की वजह से, किसी भी तरह का नुकसान नहीं होता है. ऐसा इसलिए होता है, क्योंकि इसे out के तौर पर बताया जाता है.

  1. out कीवर्ड हटाने पर, कंपाइलर addItemTo() पर कॉल करते समय गड़बड़ी का मैसेज देगा, क्योंकि Kotlin में यह पक्का नहीं हो सकता कि आप टाइप के साथ असुरक्षित काम नहीं कर रहे हैं.

दूसरा चरण: टाइप टाइप के बारे में बताना

in टाइप, out टाइप की तरह ही होता है, लेकिन सामान्य टाइप के लिए, जिन्हें सिर्फ़ फ़ंक्शन में पास किया जाता है, न कि रिटर्न. अगर आप in टाइप दिखाने की कोशिश करते हैं, तो आपको कंपाइल से जुड़ी गड़बड़ी दिखेगी. इस उदाहरण में आप एक इंटरफ़ेस के हिस्से के रूप में एक in प्रकार तय करेंगे.

  1. Aquarium.kt में ऐसा इंटरफ़ेस Cleaner तय करें जो WaterSupply तक सीमित एक सामान्य T लेता है. इसका इस्तेमाल सिर्फ़ clean() में आर्ग्युमेंट के तौर पर किया जाता है. इसलिए, आप इसे in पैरामीटर बना सकते हैं.
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. Cleaner इंटरफ़ेस का इस्तेमाल करने के लिए, TapWaterCleaner क्लास बनाएं, जो Cleaner को TapWater से जुड़े हुए मिले, क्योंकि इसमें केमिकल जोड़े जा सकते हैं.
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. Aquarium क्लास में, addWater() को T टाइप करने के लिए Cleaner अपडेट करें और जोड़ने से पहले पानी साफ़ करें.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. TapWaterCleaner, genericsExample() को TapWater के साथ बनाने के लिए, genericsExample() उदाहरण कोड को अपडेट करें. इसके बाद, क्लीनर का इस्तेमाल करके पानी जोड़ें. ज़रूरत के हिसाब से क्लीनर का इस्तेमाल किया जाएगा.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin में in और out टाइप की जानकारी का इस्तेमाल होगा, ताकि यह पक्का किया जा सके कि आपका कोड, जेनरिक कोड सुरक्षित तरीके से इस्तेमाल करे. Out और in को याद रखना आसान है: out प्रकारों को वापस लौटाए गए मानों के रूप में भेजा जा सकता है, in प्रकारों को तर्कों के रूप में अंदर की ओर भेजा जा सकता है.

अगर आप अलग-अलग तरह की समस्याओं और समस्याओं को हल करने के तरीके के बारे में ज़्यादा जानना चाहते हैं, तो दस्तावेज़ में इन चीज़ों के बारे में ज़्यादा जानकारी दी गई है.

इस टास्क में, आपको जेनरिक फ़ंक्शन और उनके इस्तेमाल के समय की जानकारी मिलेगी. आम तौर पर, जब भी फ़ंक्शन किसी सामान्य टाइप वाली क्लास का आर्ग्युमेंट लेता है, तो जेनरिक फ़ंक्शन बनाना एक अच्छा आइडिया है.

पहला चरण: जेनरिक फ़ंक्शन बनाना

  1. generics/Aquarium.kt में isWaterClean() फ़ंक्शन बनाएं, जिसमें Aquarium लगता है. आपको पैरामीटर का सामान्य टाइप तय करना होगा; WaterSupply का इस्तेमाल करना एक विकल्प है.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
   println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}

हालांकि, इसका मतलब है कि कॉल करने के लिए, Aquarium में out टाइप पैरामीटर होना ज़रूरी है. कभी-कभी out या in बहुत ज़्यादा पाबंदी वाली होता है, क्योंकि इनपुट और आउटपुट दोनों के लिए आपको टाइप का इस्तेमाल करना पड़ता है. आप फ़ंक्शन को सामान्य बनाकर out की ज़रूरी शर्त हटा सकते हैं.

  1. फ़ंक्शन को जेनरिक बनाने के लिए, कीवर्ड fun के बाद जेनरिक टाइप T और सभी कंस्ट्रेंट के साथ ऐंगल ब्रैकेट रखें. इस मामले में WaterSupply का इस्तेमाल करें. Aquarium को WaterSupply के बजाय T से सीमित होने के लिए सेट करें.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
   println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}

T, isWaterClean() के लिए एक टाइप पैरामीटर है, जिसका इस्तेमाल एक्वेरियम के सामान्य प्रकार को बताने के लिए किया जा रहा है. यह पैटर्न वाकई बहुत आम है और इस पर काम करना भी एक अच्छा आइडिया है.

  1. फ़ंक्शन के नाम के ठीक बाद और ब्रैकेट से पहले, isWaterClean() फ़ंक्शन को ऐंगल ब्रैकेट में टाइप करें.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean<TapWater>(aquarium)
}
  1. तर्क aquarium से टाइप के अनुमान की वजह से, टाइप की ज़रूरत&#39 नहीं है, इसलिए इसे हटा दें. अपना प्रोग्राम चलाएं और आउटपुट पर नज़र रखें.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

दूसरा चरण: बेहतर बनाए गए टाइप की मदद से सामान्य तरीका बनाना

आप मेथड के लिए जेनरिक फ़ंक्शन का भी इस्तेमाल कर सकते हैं, उन क्लास के लिए भी जिनका अपना खुद का जेनरिक टाइप होता है. इस चरण में, Aquarium में एक सामान्य तरीका जोड़ें, जो यह जांचता हो कि उसमें WaterSupply का टाइप है या नहीं.

  1. Aquarium क्लास में, एक तरीके का एलान करें, hasWaterSupplyOfType() जो सामान्य पैरामीटर R (T का इस्तेमाल पहले ही हो चुका है) लेता है, वह WaterSupply तक सीमित है, और waterSupply का R टाइप होने पर true दिखाता है. यह आपके बताए गए फ़ंक्शन की तरह है, लेकिन Aquarium क्लास में.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. ध्यान दें कि फ़ाइनल R को लाल रंग से अंडरलाइन किया गया है. यह देखने के लिए कि गड़बड़ी क्या है, पॉइंटर को उसके ऊपर रखें.
  2. is की जांच करने के लिए, आपको Kotlin को बताना होगा कि टाइप अपडेट किया गया है या वह असली है. फ़ंक्शन में इस्तेमाल किया जा सकता है. ऐसा करने के लिए, inline को fun कीवर्ड के सामने रखें और reified को सामान्य प्रकार R के सामने रखें.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

किसी टाइप में बदलाव किए जाने के बाद, उसे सामान्य टाइप की तरह इस्तेमाल किया जा सकता है. ऐसा इसलिए, क्योंकि इनलाइन करने के बाद यह असली टाइप का होता है. इसका मतलब है कि इस तरह का इस्तेमाल करके, is की जांच की जा सकती है.

अगर आप यहां reified का इस्तेमाल नहीं करते हैं, तो टाइप टाइप &&tt>असल में नहीं रहेगा. Kotlin के लिए, is की जांच करने की अनुमति देना ज़रूरी है. यह #&330 नहीं है, क्योंकि गैर-बदलाव किए गए प्रकार सिर्फ़ कंपाइल करने के समय उपलब्ध होते हैं और आपके प्रोग्राम से रनटाइम पर उनका इस्तेमाल नहीं किया जा सकता. इस बारे में अगले सेक्शन में ज़्यादा जानकारी दी गई है.

  1. टाइप के तौर पर TapWater पास करें. जेनरिक फ़ंक्शन को कॉल करने की तरह, फ़ंक्शन के नाम के बाद टाइप के साथ ऐंगल ब्रैकेट का इस्तेमाल करके जेनरिक तरीकों को कॉल करें. प्रोग्राम चलाएं और नतीजों पर ध्यान दें.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

तीसरा चरण: एक्सटेंशन के फ़ंक्शन बनाना

आप सामान्य फ़ंक्शन और एक्सटेंशन फ़ंक्शन के लिए, बेहतर टाइप का इस्तेमाल भी कर सकते हैं.

  1. Aquarium क्लास के बाहर, WaterSupply पर isOfType() नाम का एक एक्सटेंशन फ़ंक्शन तय करें, जो यह जांच करता हो कि पास किया गया WaterSupply किसी खास तरह का है या नहीं. उदाहरण के लिए, TapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
  1. एक्सटेंशन फ़ंक्शन को किसी तरीके की तरह कॉल करें.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.waterSupply.isOfType<TapWater>())  
}
⇒ true

इन एक्सटेंशन फ़ंक्शन के साथ, इससे कोई फ़र्क़ नहीं पड़ता कि Aquarium किस तरह का है (Aquarium या TowerTank या कुछ अन्य सब-क्लास), जब तक कि यह Aquarium है. स्टार अनुमान सिंटैक्स का इस्तेमाल करना कई तरह के मिलान बताने के लिए आसान है. जब आप स्टार- अनुमान की सुविधा का इस्तेमाल करते हैं, तो Kotlin में यह पक्का किया जाता है कि आप भी कोई असुरक्षित काम न करें.

  1. स्टार के अनुमान का इस्तेमाल करने के लिए, Aquarium के बाद <*> लगाएं. hasWaterSupplyOfType() को एक्सटेंशन फ़ंक्शन के रूप में ले जाएं, क्योंकि यह Aquarium के मुख्य एपीआई का हिस्सा नहीं है.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
  1. कॉल को hasWaterSupplyOfType() में बदलें और अपना प्रोग्राम चलाएं.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true

पिछले उदाहरण में, आपको जेनरिक टाइप को reified के तौर पर मार्क करके फ़ंक्शन inline बनाना था, क्योंकि Kotlin को रनटाइम के दौरान उनके बारे में जानने की ज़रूरत होती है, न कि सिर्फ़ समय कंपाइल करने में.

सभी सामान्य टाइप का इस्तेमाल सिर्फ़ Kotlin में कंपाइल करने के समय किया जाता है. इससे कंपाइलर यह पक्का करता है कि आप सभी काम सुरक्षित तरीके से कर रहे हैं. रनटाइम के हिसाब से सभी सामान्य टाइप को मिटा दिया जाता है. इसलिए, मिटाए गए टाइप को जांचने से पहले मिला गड़बड़ी का मैसेज.

ऐसा लगता है कि कंपाइलर रनटाइम तक जेनरिक टाइप डाले बिना सही कोड बना सकता है. इसका मतलब यह है कि कभी-कभी आप कुछ ऐसा करते हैं, जैसे कि is सामान्य टाइप की जांच करता है कि कंपाइलर काम नहीं कर सकता ##39; इस वजह से, Kotlin ने नए या असली टाइप जोड़े.

आप Kotlin दस्तावेज़ में, बदलाव किए गए टाइप और डेटा हमेशा के लिए मिटाने के बारे में ज़्यादा पढ़ सकते हैं.

यह लेसन, जेनरिक पर फ़ोकस करता है. ये कोड, ज़्यादा सुविधाजनक और इस्तेमाल करने में आसान बनाने के लिए अहम हैं.

  • कोड को ज़्यादा सुविधाजनक बनाने के लिए जेनरिक क्लास बनाएं.
  • जेनरिक के साथ इस्तेमाल किए जाने वाले टाइप को सीमित करने के लिए, जेनरिक कंस्ट्रेंट जोड़ें.
  • क्लास में पास किए जाने या उससे लौटाए जाने वाले टाइप पर पाबंदी लगाने के लिए, जेनरिक के साथ in और out टाइप का इस्तेमाल करें. इससे टाइप की बेहतर जांच करने में मदद मिलेगी.
  • जेनरिक टाइप के साथ काम करने के लिए, जेनरिक फ़ंक्शन और मेथड बनाएं. उदाहरण के लिए:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • किसी क्लास में मुख्य फ़ंक्शन जोड़ने के लिए, जेनरिक एक्सटेंशन फ़ंक्शन का इस्तेमाल करें.
  • कई तरह के डेटा को हमेशा के लिए मिटाने की वजह से, कार्ड के टाइप में बदलाव करना ज़रूरी होता है. सामान्य टाइप से अलग किए गए टाइप, लेकिन रनटाइम में दिखते हैं.
  • आपका कोड ठीक से चल रहा है या नहीं, इसकी पुष्टि करने के लिए check() फ़ंक्शन इस्तेमाल करें. उदाहरण के लिए:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

Kotlin दस्तावेज़

अगर आपको इस कोर्स के किसी विषय के बारे में ज़्यादा जानकारी चाहिए या आपको कोई समस्या आती है, तो आप https://kotlinlang.org से शुरुआत कर सकते हैं.

Kotlin ट्यूटोरियल

https://try.kotlinlang.org वेबसाइट पर, Kotlin कोन्स नाम के रिच ट्यूटोरियल शामिल हैं, जो वेब पर आधारित अनुवादक है. साथ ही, इसमें रेफ़रंस दस्तावेज़ का पूरा सेट भी शामिल है, जिसमें उदाहरण भी दिए गए हैं.

Udcity कोर्स

इस विषय पर Udacity कोर्स देखने के लिए, प्रोग्रामर के लिए Kotlin बूटकैंप देखें.

IntelliJ IDEA

JetBrains वेबसाइट पर, InliJ IDEA के दस्तावेज़ देखे जा सकते हैं.

इस सेक्शन में उन छात्र-छात्राओं के लिए गृहकार्य की असाइनमेंट की सूची दी गई है जो इस कोडलैब के ज़रिए एक शिक्षक की देखरेख में कोर्स में काम कर रहे हैं. यह क्रिएटर का काम #33 पर निर्भर करता है:

  • अगर ज़रूरी हो, तो होमवर्क असाइन करें.
  • छात्र-छात्राओं को होमवर्क के असाइनमेंट सबमिट करने के तरीके के बारे में बताएं.
  • होमवर्क असाइनमेंट को ग्रेड दें.

शिक्षक इन सुझावों का इस्तेमाल जितनी चाहें उतनी कम या ज़्यादा कर सकते हैं. साथ ही, उन्हें अपने हिसाब से कोई भी होमवर्क असाइन करना चाहिए.

अगर आप इस कोडलैब के ज़रिए खुद काम कर रहे हैं, तो बेझिझक इन होमवर्क असाइनमेंट का इस्तेमाल करें.

इन सवालों का जवाब दें सवालों

पहला सवाल

जेनरिक टाइप का नाम रखने के लिए, इनमें से कौनसा तरीका सबसे सही है?

<Gen>

<Generic>

<T>

<X>

दूसरा सवाल

सामान्य टाइप के लिए पाबंदी पर लागू होता है:

सामान्य प्रतिबंध

▢ सामान्य कंस्ट्रेंट

▢ गड़बड़ी

सामान्य टाइप की सीमा

तीसरा सवाल

बदले गए तरीके:

▢ किसी ऑब्जेक्ट के वास्तविक निष्पादन प्रभाव की गणना की गई है.

▢ इस कक्षा के लिए, प्रतिबंधित एंट्री इंडेक्स सेट किया गया है.

▢ सामान्य प्रकार का पैरामीटर असल टाइप में बनाया गया है.

▢दूरी की गड़बड़ी का संकेत ट्रिगर हुआ है.

अगले लेसन पर जाएं: 6. फ़ंक्शन में हेर-फेर

कोर्स के बारे में खास जानकारी पाने के लिए, दूसरे कोडलैब के लिंक के साथ-साथ, &kot;Kotlin बूटकैंपर के लिए प्रोग्राम: कोर्स में आपका स्वागत है.