प्रोग्रामर के लिए Kotlin बूटकैंप 4: ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग

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

परिचय

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

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

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

  • Kotlin की बुनियादी बातें, जैसे कि टाइप, ऑपरेटर, और लूपिंग
  • Kotlin में फ़ंक्शन का सिंटैक्स
  • ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग के बारे में बुनियादी जानकारी
  • IntelliJ IDEA या Android Studio जैसे आईडीई की बुनियादी बातें

आपको क्या सीखने को मिलेगा

  • Kotlin में क्लास बनाने और प्रॉपर्टी ऐक्सेस करने का तरीका
  • Kotlin में क्लास कंस्ट्रक्टर बनाने और उनका इस्तेमाल करने का तरीका
  • सबक्लास बनाने का तरीका और इनहेरिटेंस कैसे काम करता है
  • ऐब्सट्रैक्ट क्लास, इंटरफ़ेस, और इंटरफ़ेस डेलिगेशन के बारे में जानकारी
  • डेटा क्लास बनाने और उनका इस्तेमाल करने का तरीका
  • सिंगलटन, एनम, और सील की गई क्लास इस्तेमाल करने का तरीका

आपको क्या करना होगा

  • प्रॉपर्टी के साथ क्लास बनाना
  • किसी क्लास के लिए कंस्ट्रक्टर बनाना
  • कोई सबक्लास बनाना
  • ऐब्स्ट्रैक्ट क्लास और इंटरफ़ेस के उदाहरणों की जांच करना
  • एक सामान्य डेटा क्लास बनाना
  • सिंगलटन, एनम, और सील की गई क्लास के बारे में जानें

आपको प्रोग्रामिंग से जुड़े इन शब्दों के बारे में पहले से पता होना चाहिए:

  • क्लास, ऑब्जेक्ट के लिए ब्लूप्रिंट होती हैं. उदाहरण के लिए, Aquarium क्लास, ऐक्वेरियम ऑब्जेक्ट बनाने के लिए ब्लूप्रिंट है.
  • ऑब्जेक्ट, क्लास के इंस्टेंस होते हैं. ऐक्वेरियम ऑब्जेक्ट, एक असल Aquarium होता है.
  • प्रॉपर्टी, क्लास की विशेषताएं होती हैं. जैसे, किसी Aquarium की लंबाई, चौड़ाई, और ऊंचाई.
  • तरीके, जिन्हें मेंबर फ़ंक्शन भी कहा जाता है, क्लास की सुविधाएं होती हैं. तरीके वे होते हैं जो ऑब्जेक्ट के साथ "किए" जा सकते हैं. उदाहरण के लिए, किसी Aquarium ऑब्जेक्ट को fillWithWater() किया जा सकता है.
  • इंटरफ़ेस एक ऐसा स्पेसिफ़िकेशन होता है जिसे कोई क्लास लागू कर सकती है. उदाहरण के लिए, साफ़-सफ़ाई करना, ऐक्वेरियम के अलावा अन्य ऑब्जेक्ट के लिए भी सामान्य है. साथ ही, अलग-अलग ऑब्जेक्ट के लिए साफ़-सफ़ाई आम तौर पर एक ही तरीके से की जाती है. इसलिए, आपके पास Clean नाम का एक इंटरफ़ेस हो सकता है, जो clean() तरीके को तय करता है. Aquarium क्लास, Clean इंटरफ़ेस को लागू करके, मुलायम स्पंज से मछलीघर को साफ़ कर सकती है.
  • पैकेज, एक जैसे कोड को ग्रुप करने का तरीका है, ताकि उसे व्यवस्थित रखा जा सके या कोड की लाइब्रेरी बनाई जा सके. पैकेज बनाने के बाद, पैकेज के कॉन्टेंट को किसी दूसरी फ़ाइल में इंपोर्ट किया जा सकता है. साथ ही, उसमें मौजूद कोड और क्लास का फिर से इस्तेमाल किया जा सकता है.

इस टास्क में, आपको एक नया पैकेज और कुछ प्रॉपर्टी और एक तरीके वाली क्लास बनानी है.

पहला चरण: पैकेज बनाना

पैकेज की मदद से, अपने कोड को व्यवस्थित रखा जा सकता है.

  1. प्रोजेक्ट पैनल में, Hello Kotlin प्रोजेक्ट में जाकर, src फ़ोल्डर पर राइट क्लिक करें.
  2. नया > पैकेज चुनें और इसे example.myapp नाम दें.

दूसरा चरण: प्रॉपर्टी के साथ क्लास बनाना

क्लास को class कीवर्ड से तय किया जाता है. साथ ही, क्लास के नाम की शुरुआत कैपिटल लेटर से होती है.

  1. example.myapp पैकेज पर राइट क्लिक करें.
  2. नई > Kotlin फ़ाइल / क्लास चुनें.
  3. टाइप में जाकर, क्लास चुनें और क्लास का नाम Aquarium रखें. IntelliJ IDEA, फ़ाइल में पैकेज का नाम शामिल करता है और आपके लिए एक खाली Aquarium क्लास बनाता है.
  4. Aquarium क्लास में, चौड़ाई, ऊंचाई, और लंबाई (सेंटीमीटर में) के लिए var प्रॉपर्टी तय करें और उन्हें शुरू करें. प्रॉपर्टी को डिफ़ॉल्ट वैल्यू के साथ शुरू करें.
package example.myapp

class Aquarium {
    var width: Int = 20
    var height: Int = 40
    var length: Int = 100
}

Kotlin, Aquarium क्लास में तय की गई प्रॉपर्टी के लिए, अपने-आप गेटर और सेटर बना देता है. इसलिए, प्रॉपर्टी को सीधे तौर पर ऐक्सेस किया जा सकता है. उदाहरण के लिए, myAquarium.length.

तीसरा चरण: main() फ़ंक्शन बनाना

main() फ़ंक्शन को सेव करने के लिए, main.kt नाम की एक नई फ़ाइल बनाएं.

  1. बाईं ओर मौजूद प्रोजेक्ट पैन में, example.myapp पैकेज पर राइट क्लिक करें.
  2. नई > Kotlin फ़ाइल / क्लास चुनें.
  3. टाइप ड्रॉपडाउन में, फ़ाइल को चुने हुए विकल्प के तौर पर रखें. इसके बाद, फ़ाइल का नाम main.kt रखें. IntelliJ IDEA में पैकेज का नाम शामिल होता है, लेकिन इसमें किसी फ़ाइल के लिए क्लास की परिभाषा शामिल नहीं होती है.
  4. buildAquarium() फ़ंक्शन तय करें और इसके अंदर Aquarium का इंस्टेंस बनाएं. कोई इंस्टेंस बनाने के लिए, क्लास को इस तरह से रेफ़रंस करें जैसे कि वह कोई फ़ंक्शन हो, Aquarium(). इससे क्लास के कंस्ट्रक्टर को कॉल किया जाता है और Aquarium क्लास का इंस्टेंस बनाया जाता है. यह अन्य भाषाओं में new का इस्तेमाल करने जैसा ही है.
  5. main() फ़ंक्शन तय करें और buildAquarium() को कॉल करें.
package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium()
}

fun main() {
    buildAquarium()
}

चौथा चरण: कोई तरीका जोड़ना

  1. Aquarium क्लास में, एक्वेरियम की डाइमेंशन प्रॉपर्टी प्रिंट करने का तरीका जोड़ें.
    fun printSize() {
        println("Width: $width cm " +
                "Length: $length cm " +
                "Height: $height cm ")
    }
  1. main.kt में, buildAquarium() में, myAquarium पर printSize() तरीके को कॉल करें.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
}
  1. अपने प्रोग्राम को चलाने के लिए, main() फ़ंक्शन के बगल में मौजूद हरे रंग के त्रिकोण पर क्लिक करें. नतीजे देखें.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
  1. buildAquarium() में, ऊंचाई को 60 पर सेट करने के लिए कोड जोड़ें और डाइमेंशन की बदली हुई प्रॉपर्टी प्रिंट करें.
fun buildAquarium() {
    val myAquarium = Aquarium()
    myAquarium.printSize()
    myAquarium.height = 60
    myAquarium.printSize()
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 100 cm Height: 60 cm 

इस टास्क में, आपको क्लास के लिए कंस्ट्रक्टर बनाना है. साथ ही, प्रॉपर्टी के साथ काम करना जारी रखना है.

पहला चरण: कंस्ट्रक्टर बनाना

इस चरण में, आपको पहले टास्क में बनाई गई Aquarium क्लास में एक कंस्ट्रक्टर जोड़ना होता है. पिछले उदाहरण में, Aquarium के हर इंस्टेंस को एक ही डाइमेंशन के साथ बनाया गया है. डाइमेंशन बनाने के बाद, प्रॉपर्टी सेट करके उनमें बदलाव किया जा सकता है. हालांकि, शुरुआत में ही सही साइज़ का डाइमेंशन बनाना ज़्यादा आसान होता है.

कुछ प्रोग्रामिंग भाषाओं में, कंस्ट्रक्टर को क्लास में एक ऐसा तरीका बनाकर तय किया जाता है जिसका नाम क्लास के नाम जैसा ही होता है. Kotlin में, कंस्ट्रक्टर को सीधे तौर पर क्लास की परिभाषा में ही तय किया जाता है. इसमें पैरामीटर को पैरंटheses के अंदर इस तरह से तय किया जाता है जैसे क्लास कोई तरीका हो. Kotlin में फ़ंक्शन की तरह, इन पैरामीटर में डिफ़ॉल्ट वैल्यू शामिल हो सकती हैं.

  1. आपने पहले जो Aquarium क्लास बनाई थी उसमें क्लास की परिभाषा बदलें. इसमें length, width, और height के लिए डिफ़ॉल्ट वैल्यू वाले तीन कंस्ट्रक्टर पैरामीटर शामिल करें. साथ ही, उन्हें उनसे जुड़ी प्रॉपर्टी असाइन करें.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
   // Dimensions in cm
   var length: Int = length
   var width: Int = width
   var height: Int = height
...
}
  1. Kotlin में, प्रॉपर्टी को सीधे कंस्ट्रक्टर के साथ तय किया जा सकता है. इसके लिए, var या val का इस्तेमाल किया जाता है. साथ ही, Kotlin अपने-आप गेटर और सेटर भी बनाता है. इसके बाद, क्लास के मुख्य हिस्से में मौजूद प्रॉपर्टी डेफ़िनिशन को हटाया जा सकता है.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
  1. उस कंस्ट्रक्टर की मदद से Aquarium ऑब्जेक्ट बनाते समय, कोई भी आर्ग्युमेंट तय न करके डिफ़ॉल्ट वैल्यू पाई जा सकती हैं. इसके अलावा, कुछ आर्ग्युमेंट तय करके या सभी आर्ग्युमेंट तय करके, अपनी पसंद के साइज़ का Aquarium बनाया जा सकता है. buildAquarium() फ़ंक्शन में, नाम वाले पैरामीटर का इस्तेमाल करके Aquarium ऑब्जेक्ट बनाने के अलग-अलग तरीके आज़माएं.
fun buildAquarium() {
    val aquarium1 = Aquarium()
    aquarium1.printSize()
    // default height and length
    val aquarium2 = Aquarium(width = 25)
    aquarium2.printSize()
    // default width
    val aquarium3 = Aquarium(height = 35, length = 110)
    aquarium3.printSize()
    // everything custom
    val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
    aquarium4.printSize()
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm 
Width: 25 cm Length: 100 cm Height: 40 cm 
Width: 20 cm Length: 110 cm Height: 35 cm 
Width: 25 cm Length: 110 cm Height: 35 cm 

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

दूसरा चरण: init ब्लॉक जोड़ना

ऊपर दिए गए उदाहरण कंस्ट्रक्टर, सिर्फ़ प्रॉपर्टी के बारे में बताते हैं और उन्हें एक्सप्रेशन की वैल्यू असाइन करते हैं. अगर आपके कंस्ट्रक्टर को ज़्यादा शुरुआती कोड की ज़रूरत है, तो उसे एक या उससे ज़्यादा init ब्लॉक में रखा जा सकता है. इस चरण में, आपको Aquarium क्लास में कुछ init ब्लॉक जोड़ने हैं.

  1. Aquarium क्लास में, एक init ब्लॉक जोड़ें, ताकि यह प्रिंट किया जा सके कि ऑब्जेक्ट शुरू हो रहा है. साथ ही, एक दूसरा ब्लॉक जोड़ें, ताकि लीटर में वॉल्यूम प्रिंट किया जा सके.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
    init {
        println("aquarium initializing")
    }
    init {
        // 1 liter = 1000 cm^3
        println("Volume: ${width * length * height / 1000} l")
    }
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm 
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm 
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm 

ध्यान दें कि init ब्लॉक, क्लास की परिभाषा में दिखने वाले क्रम में एक्ज़ीक्यूट किए जाते हैं. साथ ही, कंस्ट्रक्टर को कॉल करने पर, ये सभी ब्लॉक एक्ज़ीक्यूट किए जाते हैं.

तीसरा चरण: सेकंडरी कंस्ट्रक्टर के बारे में जानें

इस चरण में, आपको सेकंडरी कंस्ट्रक्टर के बारे में जानकारी मिलती है. साथ ही, आपको अपनी क्लास में एक कंस्ट्रक्टर जोड़ने का तरीका भी पता चलता है. मुख्य कंस्ट्रक्टर के साथ-साथ, Kotlin क्लास में एक या उससे ज़्यादा सेकंडरी कंस्ट्रक्टर भी हो सकते हैं. मुख्य कंस्ट्रक्टर में एक या उससे ज़्यादा init ब्लॉक हो सकते हैं. सेकंडरी कंस्ट्रक्टर की मदद से, कंस्ट्रक्टर ओवरलोडिंग की जा सकती है. इसका मतलब है कि अलग-अलग आर्ग्युमेंट वाले कंस्ट्रक्टर बनाए जा सकते हैं.

  1. Aquarium क्लास में, एक सेकंडरी कंस्ट्रक्टर जोड़ें. यह कंस्ट्रक्टर, constructor कीवर्ड का इस्तेमाल करके, मछलियों की संख्या को अपने तर्क के तौर पर लेता है. मछलियों की संख्या के आधार पर, ऐक्वेरियम के अनुमानित वॉल्यूम के लिए, लीटर में val टैंक प्रॉपर्टी बनाएं. हर मछली के लिए दो लीटर (2,000 cm^3) पानी लें. साथ ही, थोड़ा ज़्यादा पानी लें, ताकि वह बाहर न गिरे.
constructor(numberOfFish: Int) : this() {
    // 2,000 cm^3 per fish + extra room so water doesn't spill
    val tank = numberOfFish * 2000 * 1.1
}
  1. सेकंडरी कंस्ट्रक्टर में, लंबाई और चौड़ाई को पहले जैसा ही रखें (जिन्हें प्राइमरी कंस्ट्रक्टर में सेट किया गया था). इसके बाद, टैंक को दिए गए वॉल्यूम का बनाने के लिए ज़रूरी ऊंचाई का हिसाब लगाएं.
    // calculate the height needed
    height = (tank / (length * width)).toInt()
  1. buildAquarium() फ़ंक्शन में, अपने नए सेकंडरी कंस्ट्रक्टर का इस्तेमाल करके Aquarium बनाने के लिए एक कॉल जोड़ें. साइज़ और वॉल्यूम प्रिंट करें.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

ध्यान दें कि वॉल्यूम को दो बार प्रिंट किया गया है. पहली बार, सेकंडरी कंस्ट्रक्टर के एक्ज़ीक्यूट होने से पहले, मुख्य कंस्ट्रक्टर में मौजूद init ब्लॉक से और दूसरी बार, buildAquarium() में मौजूद कोड से.

constructor कीवर्ड को मुख्य कंस्ट्रक्टर में भी शामिल किया जा सकता था. हालांकि, ज़्यादातर मामलों में ऐसा करना ज़रूरी नहीं होता.

चौथा चरण: नई प्रॉपर्टी गेटर जोड़ना

इस चरण में, आपको एक एक्सप्लिसिट प्रॉपर्टी गेटर जोड़ना होता है. प्रॉपर्टी तय करते समय, Kotlin अपने-आप गेटर और सेटर तय करता है. हालांकि, कभी-कभी किसी प्रॉपर्टी की वैल्यू को अडजस्ट करने या उसका हिसाब लगाने की ज़रूरत होती है. उदाहरण के लिए, ऊपर आपने Aquarium का वॉल्यूम प्रिंट किया है. वॉल्यूम को प्रॉपर्टी के तौर पर उपलब्ध कराया जा सकता है. इसके लिए, आपको एक वैरिएबल और एक गेटर तय करना होगा. volume को कैलकुलेट करना ज़रूरी है. इसलिए, गेटर को कैलकुलेट की गई वैल्यू को वापस भेजना होगा. इसके लिए, एक लाइन वाला फ़ंक्शन इस्तेमाल किया जा सकता है.

  1. Aquarium क्लास में, volume नाम की Int प्रॉपर्टी को तय करें. साथ ही, एक get() तरीका तय करें, जो अगली लाइन में वॉल्यूम का हिसाब लगाता है.
val volume: Int
    get() = width * height * length / 1000  // 1000 cm^3 = 1 l
  1. आवाज़ को प्रिंट करने वाले init ब्लॉक को हटाएं.
  2. buildAquarium() में मौजूद, आवाज़ को प्रिंट करने वाले कोड को हटाएं.
  3. printSize() तरीके में, वॉल्यूम को प्रिंट करने के लिए एक लाइन जोड़ें.
fun printSize() {
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm "
    )
    // 1 l = 1000 cm^3
    println("Volume: $volume l")
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ aquarium initializing
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l

डाइमेंशन और वॉल्यूम पहले की तरह ही हैं. हालांकि, वॉल्यूम को सिर्फ़ एक बार प्रिंट किया जाता है. ऐसा तब होता है, जब प्राइमरी कंस्ट्रक्टर और सेकंडरी कंस्ट्रक्टर, दोनों ऑब्जेक्ट को पूरी तरह से शुरू कर देते हैं.

पांचवां चरण: प्रॉपर्टी सेटर जोड़ना

इस चरण में, वॉल्यूम के लिए एक नया प्रॉपर्टी सेटर बनाया जाता है.

  1. Aquarium क्लास में, volume को var में बदलें, ताकि इसे एक से ज़्यादा बार सेट किया जा सके.
  2. गेटर के नीचे set() प्रॉपर्टी के लिए सेटर जोड़ें. इसके लिए, set() तरीके को जोड़ें. यह पानी की मात्रा के आधार पर ऊंचाई की फिर से गणना करता है.volume परंपरा के मुताबिक, सेटर पैरामीटर का नाम value होता है. हालांकि, अगर चाहें, तो इसे बदला जा सकता है.
var volume: Int
    get() = width * height * length / 1000
    set(value) {
        height = (value * 1000) / (width * length)
    }
  1. buildAquarium() में, कोड जोड़कर एक्वेरियम में पानी की मात्रा 70 लीटर पर सेट करें. नए साइज़ में प्रिंट करें.
fun buildAquarium() {
    val aquarium6 = Aquarium(numberOfFish = 29)
    aquarium6.printSize()
    aquarium6.volume = 70
    aquarium6.printSize()
}
  1. अपने प्रोग्राम को फिर से चलाएं और ऊंचाई और वॉल्यूम में हुए बदलाव को देखें.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm 
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm 
Volume: 70 l

कोड में अब तक, public या private जैसे कोई भी विज़िबिलिटी मॉडिफ़ायर नहीं हैं. ऐसा इसलिए है, क्योंकि Kotlin में डिफ़ॉल्ट रूप से सब कुछ पब्लिक होता है. इसका मतलब है कि हर चीज़ को हर जगह ऐक्सेस किया जा सकता है. इसमें क्लास, मेथड, प्रॉपर्टी, और मेंबर वैरिएबल शामिल हैं.

Kotlin में, क्लास, ऑब्जेक्ट, इंटरफ़ेस, कंस्ट्रक्टर, फ़ंक्शन, प्रॉपर्टी, और उनके सेटर के लिए विज़िबिलिटी मॉडिफ़ायर इस्तेमाल किए जा सकते हैं:

  • public का मतलब है, क्लास के बाहर दिखती है. डिफ़ॉल्ट रूप से, सब कुछ पब्लिक होता है. इसमें क्लास के वैरिएबल और मेथड शामिल हैं.
  • internal का मतलब है कि वह सिर्फ़ उस मॉड्यूल में दिखेगी. मॉड्यूल, कंपाइल की गई Kotlin फ़ाइलों का एक सेट होता है. उदाहरण के लिए, कोई लाइब्रेरी या ऐप्लिकेशन.
  • private का मतलब है कि वह सिर्फ़ क्लास के अंदर दिखेगी (या सोर्स फ़ाइल में, अगर फ़ंक्शन इस्तेमाल किए जा रहे हैं).
  • protected, private की ही तरह है, लेकिन इसे सब-क्लास में भी देखा जा सकेगा.

ज़्यादा जानकारी के लिए, Kotlin के दस्तावेज़ में Visibility Modifiers देखें.

सदस्यता वाले वैरिएबल

किसी क्लास में मौजूद प्रॉपर्टी या सदस्य वैरिएबल, डिफ़ॉल्ट रूप से public होते हैं. अगर उन्हें var के साथ तय किया जाता है, तो उनमें बदलाव किया जा सकता है. इसका मतलब है कि उन्हें पढ़ा और लिखा जा सकता है. अगर आपने उन्हें val के साथ तय किया है, तो वे शुरू होने के बाद सिर्फ़ पढ़े जा सकते हैं.

अगर आपको ऐसी प्रॉपर्टी चाहिए जिसे आपका कोड पढ़ या लिख सकता है, लेकिन बाहर का कोड सिर्फ़ पढ़ सकता है, तो प्रॉपर्टी और उसके getter को सार्वजनिक के तौर पर छोड़ा जा सकता है. साथ ही, setter को निजी के तौर पर घोषित किया जा सकता है. इसे नीचे दिखाया गया है.

var volume: Int
    get() = width * height * length / 1000
    private set(value) {
        height = (value * 1000) / (width * length)
    }

इस टास्क में, आपको Kotlin में सबक्लास और इनहेरिटेंस के काम करने के तरीके के बारे में जानकारी मिलेगी. ये अन्य भाषाओं में दिखने वाले नतीजों की तरह ही होते हैं. हालांकि, इनमें कुछ अंतर होते हैं.

Kotlin में, डिफ़ॉल्ट रूप से क्लास को सबक्लास नहीं किया जा सकता. इसी तरह, प्रॉपर्टी और सदस्य वैरिएबल को सबक्लास से बदला नहीं जा सकता. हालांकि, इन्हें ऐक्सेस किया जा सकता है.

किसी क्लास को सबक्लास करने की अनुमति देने के लिए, उसे open के तौर पर मार्क करना ज़रूरी है. इसी तरह, सबक्लास में प्रॉपर्टी और मेंबर वैरिएबल को ओवरराइड करने के लिए, उन्हें open के तौर पर मार्क करना ज़रूरी है. क्लास के इंटरफ़ेस के हिस्से के तौर पर, लागू करने से जुड़ी जानकारी को गलती से लीक होने से रोकने के लिए, open कीवर्ड का इस्तेमाल करना ज़रूरी है.

पहला चरण: Aquarium क्लास को चालू करें

इस चरण में, आपको Aquarium क्लास open बनानी होगी, ताकि अगले चरण में इसे बदला जा सके.

  1. Aquarium क्लास और उसकी सभी प्रॉपर्टी को open कीवर्ड के साथ मार्क करें.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
    open var volume: Int
        get() = width * height * length / 1000
        set(value) {
            height = (value * 1000) / (width * length)
        }
  1. shape वैल्यू वाली ओपन shape प्रॉपर्टी जोड़ें."rectangle"
   open val shape = "rectangle"
  1. ऐसी खुली water प्रॉपर्टी जोड़ें जिसमें गैटर हो. यह गैटर, Aquarium के वॉल्यूम का 90% दिखाता हो.
    open var water: Double = 0.0
        get() = volume * 0.9
  1. आकार और पानी की मात्रा को वॉल्यूम के प्रतिशत के तौर पर प्रिंट करने के लिए, printSize() तरीके में कोड जोड़ें.
fun printSize() {
    println(shape)
    println("Width: $width cm " +
            "Length: $length cm " +
            "Height: $height cm ")
    // 1 l = 1000 cm^3
    println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
  1. buildAquarium() में, कोड बदलकर width = 25, length = 25, और height = 40 के साथ Aquarium बनाएं.
fun buildAquarium() {
    val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
    aquarium6.printSize()
}
  1. अपने प्रोग्राम को चलाएं और नए आउटपुट को देखें.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)

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

  1. Aquarium की एक सबक्लास TowerTank बनाएं. इसमें रेक्टैंगुलर टैंक के बजाय, गोल सिलेंडर टैंक का इस्तेमाल किया जाता है. TowerTank को Aquarium के नीचे जोड़ा जा सकता है, क्योंकि Aquarium क्लास की तरह ही एक ही फ़ाइल में दूसरी क्लास जोड़ी जा सकती है.
  2. कंस्ट्रक्टर में तय की गई height प्रॉपर्टी को TowerTank में ओवरराइड करें. किसी प्रॉपर्टी को बदलने के लिए, सबक्लास में override कीवर्ड का इस्तेमाल करें.
  1. TowerTank के कंस्ट्रक्टर को diameter लेने के लिए बनाएं. Aquarium सुपरक्लास में कंस्ट्रक्टर को कॉल करते समय, length और width, दोनों के लिए diameter का इस्तेमाल करें.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
  1. सिलिंडर का हिसाब लगाने के लिए, वॉल्यूम प्रॉपर्टी को बदलें. बेलन का फ़ॉर्मूला, पाई गुणा त्रिज्या का वर्ग गुणा ऊँचाई होता है. आपको java.lang.Math से PI कॉन्स्टेंट इंपोर्ट करना होगा.
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }
  1. TowerTank में, water प्रॉपर्टी को बदलकर वॉल्यूम का 80% करें.
override var water = volume * 0.8
  1. shape को "cylinder" के तौर पर बदलें.
override val shape = "cylinder"
  1. आपकी फ़ाइनल TowerTank क्लास, नीचे दिए गए कोड की तरह दिखनी चाहिए.

Aquarium.kt:

package example.myapp

import java.lang.Math.PI

... // existing Aquarium class

class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
    override var volume: Int
    // ellipse area = π * r1 * r2
    get() = (width/2 * length/2 * height / 1000 * PI).toInt()
    set(value) {
        height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
    }

    override var water = volume * 0.8
    override val shape = "cylinder"
}
  1. buildAquarium() में, 25 सेमी व्यास और 45 सेमी ऊंचाई वाला TowerTank बनाओ. साइज़ प्रिंट करें.

main.kt:

package example.myapp

fun buildAquarium() {
    val myAquarium = Aquarium(width = 25, length = 25, height = 40)
    myAquarium.printSize()
    val myTower = TowerTank(diameter = 25, height = 40)
    myTower.printSize()
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ aquarium initializing
rectangle
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 25 l Water: 22.5 l (90.0% full)
aquarium initializing
cylinder
Width: 25 cm Length: 25 cm Height: 40 cm 
Volume: 18 l Water: 14.4 l (80.0% full)

कभी-कभी आपको कुछ मिलती-जुलती क्लास के लिए, सामान्य व्यवहार या प्रॉपर्टी तय करनी होती हैं. Kotlin में ऐसा करने के दो तरीके हैं: इंटरफ़ेस और ऐब्सट्रैक्ट क्लास. इस टास्क में, आपको एक ऐब्स्ट्रैक्ट AquariumFish क्लास बनानी है. इसमें ऐसी प्रॉपर्टी शामिल होंगी जो सभी मछलियों में एक जैसी होती हैं. आपने FishAction नाम का एक इंटरफ़ेस बनाया है, ताकि सभी मछलियों के लिए एक जैसा व्यवहार तय किया जा सके.

  • न तो ऐब्स्ट्रैक्ट क्लास और न ही इंटरफ़ेस को अपने-आप इंस्टैंशिएट किया जा सकता है. इसका मतलब है कि सीधे तौर पर उन टाइप के ऑब्जेक्ट नहीं बनाए जा सकते.
  • ऐब्स्ट्रैक्ट क्लास में कंस्ट्रक्टर होते हैं.
  • इंटरफ़ेस में कोई कंस्ट्रक्टर लॉजिक नहीं हो सकता. साथ ही, वे किसी भी स्थिति को सेव नहीं कर सकते.

पहला चरण. ऐब्स्ट्रैक्ट क्लास बनाना

  1. example.myapp में जाकर, एक नई फ़ाइल AquariumFish.kt बनाएं.
  2. AquariumFish नाम की क्लास बनाएं और उसे abstract के तौर पर मार्क करें.
  3. एक String प्रॉपर्टी, color जोड़ें और उसे abstract के तौर पर मार्क करें.
package example.myapp

abstract class AquariumFish {
    abstract val color: String
}
  1. AquariumFish, Shark, और Plecostomus की दो सबक्लास बनाएं.
  2. color ऐब्स्ट्रैक्ट है. इसलिए, सब-क्लास को इसे लागू करना होगा. Shark को स्लेटी और Plecostomus को सुनहरा रंग दें.
class Shark: AquariumFish() {
    override val color = "gray"
}

class Plecostomus: AquariumFish() {
    override val color = "gold"
}
  1. अपनी क्लास को टेस्ट करने के लिए, main.kt में makeFish() फ़ंक्शन बनाएं. Shark और Plecostomus को इंस्टैंशिएट करें. इसके बाद, हर एक का रंग प्रिंट करें.
  2. main() में मौजूद अपने पिछले टेस्ट कोड को मिटाएं और makeFish() में कॉल जोड़ें. आपका कोड, यहां दिए गए कोड की तरह दिखना चाहिए.

main.kt:

package example.myapp

fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()

    println("Shark: ${shark.color}")
    println("Plecostomus: ${pleco.color}")
}

fun main () {
    makeFish()
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ Shark: gray 
Plecostomus: gold

इस डायग्राम में, Shark क्लास और Plecostomus क्लास को दिखाया गया है. ये दोनों, ऐब्स्ट्रैक्ट क्लास AquariumFish की सबक्लास हैं.

इस डायग्राम में, ऐब्स्ट्रैक्ट क्लास, AquariumFish, और दो सबक्लास, Shark और Plecostumus दिखाए गए हैं.

दूसरा चरण. इंटरफ़ेस बनाना

  1. AquariumFish.kt में, eat() तरीके के साथ FishAction नाम का इंटरफ़ेस बनाएं.
interface FishAction  {
    fun eat()
}
  1. हर सबक्लास में FishAction जोड़ें. साथ ही, eat() को लागू करें, ताकि यह प्रिंट कर सके कि मछली क्या करती है.
class Shark: AquariumFish(), FishAction {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

class Plecostomus: AquariumFish(), FishAction {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. makeFish() फ़ंक्शन में, बनाई गई हर मछली को कुछ खाने के लिए दें. इसके लिए, eat() को कॉल करें.
fun makeFish() {
    val shark = Shark()
    val pleco = Plecostomus()
    println("Shark: ${shark.color}")
    shark.eat()
    println("Plecostomus: ${pleco.color}")
    pleco.eat()
}
  1. प्रोग्राम चलाएं और आउटपुट देखें.
⇒ Shark: gray
hunt and eat fish
Plecostomus: gold
eat algae

इस डायग्राम में Shark क्लास और Plecostomus क्लास को दिखाया गया है. ये दोनों क्लास, FishAction इंटरफ़ेस से बनी हैं और इसे लागू करती हैं.

ऐब्सट्रैक्ट क्लास और इंटरफ़ेस का इस्तेमाल कब करना चाहिए

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

ऊपर बताया गया है कि ऐब्स्ट्रैक्ट क्लास में कंस्ट्रक्टर हो सकते हैं, जबकि इंटरफ़ेस में नहीं. हालांकि, इसके अलावा ये दोनों काफ़ी मिलते-जुलते हैं. इसलिए, आपको इनमें से हर एक का इस्तेमाल कब करना चाहिए?

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

कंपोज़िशन से अक्सर बेहतर इनकैप्सुलेशन, कम कपलिंग (एक-दूसरे पर निर्भरता), साफ़ इंटरफ़ेस, और ज़्यादा इस्तेमाल किया जा सकने वाला कोड मिलता है. इन वजहों से, इंटरफ़ेस के साथ कंपोज़िशन का इस्तेमाल करना बेहतर डिज़ाइन है. दूसरी ओर, कुछ समस्याओं के लिए ऐब्स्ट्रैक्ट क्लास से इनहेरिटेंस, एक बेहतर विकल्प होता है. इसलिए, आपको कंपोज़िशन का इस्तेमाल करना चाहिए. हालांकि, अगर इनहेरिटेंस का इस्तेमाल करना ज़रूरी हो, तो Kotlin में ऐसा किया जा सकता है!

  • अगर आपके पास कई तरीके हैं और एक या दो डिफ़ॉल्ट लागू करने के तरीके हैं, तो इंटरफ़ेस का इस्तेमाल करें. उदाहरण के लिए, AquariumAction में दिए गए तरीके का इस्तेमाल करें.
interface AquariumAction {
    fun eat()
    fun jump()
    fun clean()
    fun catchFish()
    fun swim()  {
        println("swim")
    }
}
  • जब किसी क्लास को पूरा न किया जा सके, तब ऐब्स्ट्रैक्ट क्लास का इस्तेमाल करें. उदाहरण के लिए, AquariumFish क्लास पर वापस जाएं. यहां सभी AquariumFish, FishAction को लागू कर सकते हैं. साथ ही, eat के लिए डिफ़ॉल्ट तौर पर लागू होने वाला कोड उपलब्ध करा सकते हैं. हालांकि, color को ऐब्स्ट्रैक्ट छोड़ दें, क्योंकि मछली के लिए कोई डिफ़ॉल्ट रंग नहीं होता.
interface FishAction  {
    fun eat()
}

abstract class AquariumFish: FishAction {
   abstract val color: String
   override fun eat() = println("yum")
}

पिछले टास्क में, ऐब्स्ट्रैक्ट क्लास, इंटरफ़ेस, और कंपोज़िशन के बारे में बताया गया था. इंटरफ़ेस डेलिगेशन एक बेहतर तकनीक है. इसमें किसी इंटरफ़ेस के तरीकों को हेल्पर (या डेलिगेट) ऑब्जेक्ट लागू करता है. इसके बाद, इसका इस्तेमाल क्लास करती है. यह तकनीक तब काम आ सकती है, जब आपको किसी इंटरफ़ेस का इस्तेमाल, एक जैसी नहीं बल्कि अलग-अलग क्लास में करना हो. इसके लिए, आपको इंटरफ़ेस की ज़रूरी फ़ंक्शनैलिटी को किसी अलग हेल्पर क्लास में जोड़ना होगा. इसके बाद, हर क्लास, हेल्पर क्लास के इंस्टेंस का इस्तेमाल करके फ़ंक्शनैलिटी को लागू करेगी.

इस टास्क में, इंटरफ़ेस डेलिगेशन का इस्तेमाल करके किसी क्लास में फ़ंक्शन जोड़ा जाता है.

पहला चरण: नया इंटरफ़ेस बनाना

  1. AquariumFish.kt में, AquariumFish क्लास हटाएं. AquariumFish क्लास से इनहेरिट करने के बजाय, Plecostomus और Shark, मछली की गतिविधि और उसके रंग, दोनों के लिए इंटरफ़ेस लागू करने वाले हैं.
  2. एक नया इंटरफ़ेस, FishColor बनाएं, जो रंग को स्ट्रिंग के तौर पर तय करता है.
interface FishColor {
    val color: String
}
  1. दो इंटरफ़ेस, FishAction, और FishColor लागू करने के लिए, Plecostomus बदलें. आपको FishColor से color और FishAction से eat() को बदलना होगा.
class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}
  1. AquariumFish से इनहेरिट करने के बजाय, Shark क्लास को बदलकर FishAction और FishColor, दोनों इंटरफ़ेस लागू करें.
class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}
  1. आपका पूरा कोड कुछ ऐसा दिखना चाहिए:
package example.myapp

interface FishAction {
    fun eat()
}

interface FishColor {
    val color: String
}

class Plecostomus: FishAction, FishColor {
    override val color = "gold"
    override fun eat() {
        println("eat algae")
    }
}

class Shark: FishAction, FishColor {
    override val color = "gray"
    override fun eat() {
        println("hunt and eat fish")
    }
}

दूसरा चरण: सिंगलटन क्लास बनाना

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

GoldColor के एक से ज़्यादा इंस्टेंस बनाने का कोई मतलब नहीं है, क्योंकि वे सभी एक ही काम करेंगे. इसलिए, Kotlin में ऐसी क्लास का एलान किया जा सकता है जिसमें class के बजाय object कीवर्ड का इस्तेमाल करके, सिर्फ़ एक इंस्टेंस बनाया जा सकता है. Kotlin उस एक इंस्टेंस को बनाएगा. साथ ही, उस इंस्टेंस को क्लास के नाम से रेफ़र किया जाएगा. इसके बाद, अन्य सभी ऑब्जेक्ट सिर्फ़ इस एक इंस्टेंस का इस्तेमाल कर सकते हैं. इस क्लास के अन्य इंस्टेंस बनाने का कोई तरीका नहीं है. अगर आपको सिंगलटन पैटर्न के बारे में पता है, तो Kotlin में सिंगलटन को इस तरह लागू किया जाता है.

  1. AquariumFish.kt में, GoldColor के लिए एक ऑब्जेक्ट बनाएं. रंग को बदलें.
object GoldColor : FishColor {
   override val color = "gold"
}

तीसरा चरण: FishColor के लिए इंटरफ़ेस डेलिगेशन जोड़ना

अब इंटरफ़ेस डेलिगेशन का इस्तेमाल किया जा सकता है.

  1. AquariumFish.kt में, Plecostomus से color का ओवरराइड हटाएं.
  2. Plecostomus क्लास को बदलकर GoldColor करें, ताकि उसका रंग GoldColor से मिल सके. इसके लिए, क्लास के एलान में by GoldColor जोड़ें और डेलिगेशन बनाएं. इसका मतलब है कि FishColor को लागू करने के बजाय, GoldColor की ओर से उपलब्ध कराए गए तरीके का इस्तेमाल करें. इसलिए, जब भी color को ऐक्सेस किया जाता है, तो इसे GoldColor को सौंप दिया जाता है.
class Plecostomus:  FishAction, FishColor by GoldColor {
   override fun eat() {
       println("eat algae")
   }
}

क्लास के हिसाब से, सभी प्लेको सुनहरे रंग के होंगे. हालांकि, ये मछलियां कई रंगों में मिलती हैं. इसके लिए, कंस्ट्रक्टर पैरामीटर में रंग की जानकारी जोड़ें. साथ ही, Plecostomus के लिए डिफ़ॉल्ट रंग के तौर पर GoldColor को सेट करें.

  1. Plecostomus क्लास को बदलकर, कंस्ट्रक्टर के साथ fishColor में पास किया गया क्लास करें. साथ ही, इसे डिफ़ॉल्ट रूप से GoldColor पर सेट करें. प्रतिनिधि के तौर पर काम करने की अनुमति को by GoldColor से बदलकर by fishColor करें.
class Plecostomus(fishColor: FishColor = GoldColor):  FishAction,
       FishColor by fishColor {
   override fun eat() {
       println("eat algae")
   }
}

चौथा चरण: FishAction के लिए इंटरफ़ेस डेलिगेशन जोड़ना

इसी तरह, FishAction के लिए इंटरफ़ेस डेलिगेशन का इस्तेमाल किया जा सकता है.

  1. AquariumFish.kt में, PrintingFishAction क्लास बनाएं. यह FishAction को लागू करती है. इसमें String और food शामिल हैं. इसके बाद, यह प्रिंट करती है कि मछली क्या खाती है.
class PrintingFishAction(val food: String) : FishAction {
    override fun eat() {
        println(food)
    }
}
  1. Plecostomus क्लास में, ओवरराइड फ़ंक्शन eat() को हटाएं, क्योंकि आपको इसे डेलिगेशन से बदलना है.
  2. Plecostomus के एलान में, FishAction को PrintingFishAction को सौंपें और "eat algae" पास करें.
  3. सभी डेलिगेशन के साथ, Plecostomus क्लास के मुख्य हिस्से में कोई कोड नहीं है. इसलिए, {} को हटा दें, क्योंकि सभी ओवरराइड इंटरफ़ेस डेलिगेशन से मैनेज किए जाते हैं
class Plecostomus (fishColor: FishColor = GoldColor):
        FishAction by PrintingFishAction("eat algae"),
        FishColor by fishColor

इस डायग्राम में, Shark और Plecostomus क्लास को दिखाया गया है. ये दोनों, PrintingFishAction और FishColor इंटरफ़ेस से बनी हैं. हालांकि, ये इंटरफ़ेस को लागू करने का काम सौंपती हैं.

इंटरफ़ेस डेलिगेशन एक बेहतरीन सुविधा है. इसलिए, जब भी आपको किसी दूसरी भाषा में ऐब्स्ट्रैक्ट क्लास का इस्तेमाल करना हो, तब आपको आम तौर पर यह सोचना चाहिए कि इसका इस्तेमाल कैसे किया जाए. इसकी मदद से, कई सबक्लास बनाने के बजाय, कंपोज़िशन का इस्तेमाल करके व्यवहार को प्लग इन किया जा सकता है. हर सबक्लास अलग-अलग तरीके से काम करता है.

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

पहला चरण: डेटा क्लास बनाना

  1. नया कोड रखने के लिए, example.myapp पैकेज में नया पैकेज decor जोड़ें. प्रोजेक्ट पैन में मौजूद example.myapp पर राइट क्लिक करें. इसके बाद, File > New > Package को चुनें.
  2. पैकेज में, Decoration नाम की एक नई क्लास बनाएं.
package example.myapp.decor

class Decoration {
}
  1. किसी क्लास को Decoration डेटा क्लास बनाने के लिए, क्लास के एलान से पहले data कीवर्ड का इस्तेमाल करें.
  2. क्लास को कुछ डेटा देने के लिए, String नाम की String प्रॉपर्टी जोड़ें.rocks
data class Decoration(val rocks: String) {
}
  1. क्लास से बाहर फ़ाइल में, makeDecorations() फ़ंक्शन जोड़ें. इससे "granite" के साथ Decoration का इंस्टेंस बनाया और प्रिंट किया जा सकता है.
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)
}
  1. makeDecorations() को कॉल करने के लिए, main() फ़ंक्शन जोड़ें और अपना प्रोग्राम चलाएं. ध्यान दें कि यह एक डेटा क्लास है, इसलिए इससे काम का आउटपुट जनरेट हुआ है.
⇒ Decoration(rocks=granite)
  1. makeDecorations() में, "स्लेट" वाले दो और Decoration ऑब्जेक्ट इंस्टैंशिएट करें और उन्हें प्रिंट करें.
fun makeDecorations() {
    val decoration1 = Decoration("granite")
    println(decoration1)

    val decoration2 = Decoration("slate")
    println(decoration2)

    val decoration3 = Decoration("slate")
    println(decoration3)
}
  1. makeDecorations() में, एक प्रिंट स्टेटमेंट जोड़ें. यह स्टेटमेंट, decoration1 की तुलना decoration2 से करने पर मिले नतीजे को प्रिंट करता है. साथ ही, एक और स्टेटमेंट जोड़ें, जो decoration3 की तुलना decoration2 से करने पर मिले नतीजे को प्रिंट करता है. डेटा क्लास के ज़रिए उपलब्ध कराए गए equals() तरीके का इस्तेमाल करें.
    println (decoration1.equals(decoration2))
    println (decoration3.equals(decoration2))
  1. अपना कोड लागू करें.
⇒ Decoration(rocks=granite)
Decoration(rocks=slate)
Decoration(rocks=slate)
false
true

दूसरा चरण. डीस्ट्रक्चरिंग का इस्तेमाल करना

किसी डेटा ऑब्जेक्ट की प्रॉपर्टी पाने और उन्हें वैरिएबल को असाइन करने के लिए, उन्हें एक-एक करके असाइन किया जा सकता है. जैसे, इस तरह.

val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver

इसके बजाय, हर प्रॉपर्टी के लिए एक वैरिएबल बनाया जा सकता है. साथ ही, डेटा ऑब्जेक्ट को वैरिएबल के ग्रुप को असाइन किया जा सकता है. Kotlin, प्रॉपर्टी की वैल्यू को हर वैरिएबल में डालता है.

val (rock, wood, diver) = decoration

इसे डीस्ट्रक्चरिंग कहा जाता है. यह एक उपयोगी शॉर्टहैंड है. वैरिएबल की संख्या, प्रॉपर्टी की संख्या से मेल खानी चाहिए. साथ ही, वैरिएबल को उसी क्रम में असाइन किया जाता है जिस क्रम में उन्हें क्लास में शामिल किया गया है. यहां एक पूरा उदाहरण दिया गया है, जिसे Decoration.kt में आज़माया जा सकता है.

// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}

fun makeDecorations() {
    val d5 = Decoration2("crystal", "wood", "diver")
    println(d5)

// Assign all properties to variables.
    val (rock, wood, diver) = d5
    println(rock)
    println(wood)
    println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver)
crystal
wood
diver

अगर आपको एक या उससे ज़्यादा प्रॉपर्टी की ज़रूरत नहीं है, तो उन्हें छोड़ा जा सकता है. इसके लिए, वैरिएबल के नाम की जगह _ का इस्तेमाल करें. ऐसा नीचे दिए गए कोड में दिखाया गया है.

    val (rock, _, diver) = d5

इस टास्क में, आपको Kotlin में मौजूद कुछ खास क्लास के बारे में जानकारी मिलेगी. इनमें ये शामिल हैं:

  • सिंगलटन क्लास
  • Enums
  • सील की गई क्लास

पहला चरण: सिंगलटन क्लास को याद करना

GoldColor क्लास का इस्तेमाल करके बनाए गए पिछले उदाहरण को याद करें.

object GoldColor : FishColor {
   override val color = "gold"
}

GoldColor का हर इंस्टेंस एक ही काम करता है. इसलिए, इसे class के बजाय object के तौर पर एलान किया जाता है, ताकि इसे सिंगलटन बनाया जा सके. इसका सिर्फ़ एक इंस्टेंस हो सकता है.

दूसरा चरण: कोई enum बनाना

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

  1. Decoration.kt में, किसी enum का उदाहरण आज़माएं.
enum class Color(val rgb: Int) {
   RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}

एनम, सिंगलटन की तरह होते हैं. इनमें सिर्फ़ एक वैल्यू हो सकती है. साथ ही, हर वैल्यू के लिए सिर्फ़ एक एनम हो सकता है. उदाहरण के लिए, सिर्फ़ एक Color.RED, एक Color.GREEN, और एक Color.BLUE हो सकता है. इस उदाहरण में, रंग के कॉम्पोनेंट दिखाने के लिए, आरजीबी वैल्यू को rgb प्रॉपर्टी असाइन किया गया है. ordinal प्रॉपर्टी का इस्तेमाल करके, किसी enum की ऑर्डिनल वैल्यू भी पाई जा सकती है. साथ ही, name प्रॉपर्टी का इस्तेमाल करके, उसका नाम भी पाया जा सकता है.

  1. एनम का कोई दूसरा उदाहरण आज़माएं.
enum class Direction(val degrees: Int) {
    NORTH(0), SOUTH(180), EAST(90), WEST(270)
}

fun main() {
    println(Direction.EAST.name)
    println(Direction.EAST.ordinal)
    println(Direction.EAST.degrees)
}
⇒ EAST
2
90

तीसरा चरण: सील की गई क्लास बनाना

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

क्लास और सब-क्लास एक ही फ़ाइल में हैं. इसलिए, Kotlin को सभी सब-क्लास के बारे में स्टैटिक तौर पर पता चल जाएगा. इसका मतलब है कि कंपाइल टाइम पर, कंपाइलर सभी क्लास और सबक्लास को देखता है और उसे पता होता है कि ये सभी क्लास और सबक्लास हैं. इसलिए, कंपाइलर आपके लिए अतिरिक्त जांच कर सकता है.

  1. AquariumFish.kt में, सील की गई क्लास का एक उदाहरण आज़माएं. इसमें पानी में रहने वाले जीवों के बारे में बताया गया है.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()

fun matchSeal(seal: Seal): String {
   return when(seal) {
       is Walrus -> "walrus"
       is SeaLion -> "sea lion"
   }
}

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

इस लेसन में कई विषयों को शामिल किया गया है. ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग की अन्य भाषाओं की तरह ही, Kotlin में भी कई सुविधाएं उपलब्ध हैं. हालांकि, Kotlin में कुछ ऐसी सुविधाएं भी हैं जिनकी मदद से कोड को छोटा और आसानी से समझा जा सकता है.

क्लास और कंस्ट्रक्टर

  • class का इस्तेमाल करके, Kotlin में किसी क्लास को परिभाषित करें.
  • Kotlin, प्रॉपर्टी के लिए सेटर और गैटर अपने-आप बनाता है.
  • मुख्य कंस्ट्रक्टर को सीधे तौर पर क्लास की परिभाषा में तय करें. उदाहरण के लिए:
    class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
  • अगर मुख्य कंस्ट्रक्टर को अतिरिक्त कोड की ज़रूरत है, तो उसे एक या उससे ज़्यादा init ब्लॉक में लिखें.
  • कोई क्लास, constructor का इस्तेमाल करके एक या उससे ज़्यादा सेकंडरी कंस्ट्रक्टर तय कर सकती है. हालांकि, Kotlin के स्टाइल गाइड के मुताबिक, फ़ैक्ट्री फ़ंक्शन का इस्तेमाल करना बेहतर होता है.

विज़िबिलिटी मॉडिफ़ायर और सबक्लास

  • Kotlin में सभी क्लास और फ़ंक्शन डिफ़ॉल्ट रूप से public होते हैं. हालांकि, मॉडिफ़ायर का इस्तेमाल करके, विज़िबिलिटी को internal, private या protected में बदला जा सकता है.
  • सबक्लास बनाने के लिए, पैरंट क्लास को open के तौर पर मार्क किया जाना चाहिए.
  • किसी सबक्लास में मेथड और प्रॉपर्टी को ओवरराइड करने के लिए, पैरंट क्लास में मेथड और प्रॉपर्टी को open के तौर पर मार्क किया जाना चाहिए.
  • किसी सील की गई क्लास को सिर्फ़ उसी फ़ाइल में सबक्लास किया जा सकता है जिसमें उसे तय किया गया है. sealed प्रीफ़िक्स का इस्तेमाल करके, सील की गई क्लास बनाएं.

डेटा क्लास, सिंगलटन, और enum

  • डेटा क्लास बनाने के लिए, एलान से पहले data जोड़ें.
  • डीस्ट्रक्चरिंग, data ऑब्जेक्ट की प्रॉपर्टी को अलग-अलग वैरिएबल असाइन करने का एक छोटा तरीका है.
  • class के बजाय object का इस्तेमाल करके, सिंगलटन क्लास बनाएं.
  • enum class का इस्तेमाल करके, enum को तय करें.

ऐब्स्ट्रैक्ट क्लास, इंटरफ़ेस, और डेलिगेशन

  • ऐब्स्ट्रैक्ट क्लास और इंटरफ़ेस, क्लास के बीच सामान्य व्यवहार शेयर करने के दो तरीके हैं.
  • ऐब्स्ट्रैक्ट क्लास, प्रॉपर्टी और व्यवहार को तय करती है. हालांकि, यह सब-क्लास को लागू करने के लिए छोड़ देती है.
  • इंटरफ़ेस, व्यवहार को तय करता है. साथ ही, यह कुछ या सभी व्यवहारों के लिए डिफ़ॉल्ट तौर पर लागू होने वाली सेटिंग दे सकता है.
  • किसी क्लास को कंपोज़ करने के लिए इंटरफ़ेस का इस्तेमाल करने पर, क्लास में मौजूद क्लास इंस्टेंस की मदद से क्लास की सुविधाओं को बढ़ाया जाता है.
  • इंटरफ़ेस डेलिगेशन में कंपोज़िशन का इस्तेमाल किया जाता है. हालांकि, यह इंटरफ़ेस क्लास को भी लागू करने का अधिकार देता है.
  • कंपोज़िशन, इंटरफ़ेस डेलिगेशन का इस्तेमाल करके किसी क्लास में फ़ंक्शन जोड़ने का एक बेहतरीन तरीका है. आम तौर पर, कंपोज़िशन को प्राथमिकता दी जाती है. हालांकि, कुछ समस्याओं के लिए ऐब्स्ट्रैक्ट क्लास से इनहेरिटेंस बेहतर होता है.

Kotlin का दस्तावेज़

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

Kotlin के ट्यूटोरियल

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

Udacity कोर्स

इस विषय पर Udacity का कोर्स देखने के लिए, Kotlin Bootcamp for Programmers पर जाएं.

IntelliJ IDEA

IntelliJ IDEA के लिए दस्तावेज़, JetBrains की वेबसाइट पर उपलब्ध हैं.

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

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

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

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

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

पहला सवाल

क्लास में एक खास तरीका होता है. यह तरीका, उस क्लास के ऑब्जेक्ट बनाने के लिए ब्लूप्रिंट के तौर पर काम करता है. इस तरीके को क्या कहा जाता है?

▢ बिल्डर

▢ इंस्टैंशिएट करने वाला

▢ कंस्ट्रक्टर

▢ ब्लूप्रिंट

दूसरा सवाल

इंटरफ़ेस और ऐब्स्ट्रैक्ट क्लास के बारे में इनमें से कौनसा स्टेटमेंट सही नहीं है?

▢ ऐब्स्ट्रैक्ट क्लास में कंस्ट्रक्टर हो सकते हैं.

▢ इंटरफ़ेस में कंस्ट्रक्टर नहीं हो सकते.

▢ इंटरफ़ेस और ऐब्स्ट्रैक्ट क्लास को सीधे तौर पर इंस्टैंशिएट किया जा सकता है.

▢ ऐब्स्ट्रैक्ट प्रॉपर्टी को ऐब्स्ट्रैक्ट क्लास की सब-क्लास से लागू किया जाना चाहिए.

तीसरा सवाल

इनमें से कौनसा, Kotlin में प्रॉपर्टी, तरीकों वगैरह के लिए विज़िबिलिटी मॉडिफ़ायर नहीं है?

internal

nosubclass

protected

private

चौथा सवाल

इस डेटा क्लास पर विचार करें:
data class Fish(val name: String, val species:String, val colors:String)
इनमें से कौनसा कोड, Fish ऑब्जेक्ट बनाने और उसे डिस्ट्रक्चर करने के लिए मान्य नहीं है?

val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")

val (name2, _, colors2) = Fish("Bitey", "shark", "gray")

val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")

val (name4, species4, colors4) = Fish("Harry", "halibut")

पांचवां सवाल

मान लें कि आपके पास एक चिड़ियाघर है, जिसमें कई जानवर हैं और उन सभी की देखभाल करनी है. इनमें से कौनसी चीज़, देखभाल करने के सिद्धांत को लागू करने का हिस्सा नहीं होगी?

▢ जानवरों के खाने के अलग-अलग तरह के भोजन के लिए interface.

▢ एक abstract Caretaker क्लास, जिससे अलग-अलग तरह के केयरटेकर बनाए जा सकते हैं.

▢ किसी जानवर को साफ़ पानी देने के लिए interface.

▢ फ़ीडिंग शेड्यूल में एंट्री के लिए data क्लास.

अगले लेसन पर जाएं: 5.1 एक्सटेंशन

कोर्स की खास जानकारी और अन्य कोडलैब के लिंक देखने के लिए, "प्रोग्रामर के लिए Kotlin बूटकैंप: कोर्स में आपका स्वागत है." लेख पढ़ें