প্রোগ্রামারদের জন্য কোটলিন বুটক্যাম্প 5.2: জেনেরিক

এই কোডল্যাবটি প্রোগ্রামারদের কোর্সের জন্য কোটলিন বুটক্যাম্পের অংশ। আপনি যদি ক্রমানুসারে কোডল্যাবগুলির মাধ্যমে কাজ করেন তবে আপনি এই কোর্সের সর্বাধিক মূল্য পাবেন৷ আপনার জ্ঞানের উপর নির্ভর করে, আপনি কিছু বিভাগ স্কিম করতে সক্ষম হতে পারেন। এই কোর্সটি এমন প্রোগ্রামারদের জন্য তৈরি যারা একটি অবজেক্ট-ওরিয়েন্টেড ভাষা জানেন এবং কোটলিন শিখতে চান।

ভূমিকা

এই কোডল্যাবে আপনাকে জেনেরিক ক্লাস, ফাংশন এবং পদ্ধতি এবং তারা কোটলিনে কীভাবে কাজ করে তার সাথে পরিচয় করিয়ে দেওয়া হয়েছে।

একটি একক নমুনা অ্যাপ তৈরি করার পরিবর্তে, এই কোর্সের পাঠগুলি আপনার জ্ঞান তৈরি করার জন্য ডিজাইন করা হয়েছে, তবে একে অপরের থেকে আধা-স্বতন্ত্র থাকুন যাতে আপনি আপনার পরিচিত বিভাগগুলিকে স্কিম করতে পারেন। তাদের একসাথে বাঁধতে, অনেক উদাহরণ একটি অ্যাকোয়ারিয়াম থিম ব্যবহার করে। এবং আপনি যদি সম্পূর্ণ অ্যাকোয়ারিয়ামের গল্প দেখতে চান, তাহলে প্রোগ্রামারদের জন্য কোটলিন বুটক্যাম্প উদাসিটি কোর্সটি দেখুন।

আপনি ইতিমধ্যে কি জানা উচিত

  • কোটলিন ফাংশন, ক্লাস এবং পদ্ধতির সিনট্যাক্স
  • কিভাবে IntelliJ IDEA এ একটি নতুন ক্লাস তৈরি করবেন এবং একটি প্রোগ্রাম চালাবেন

আপনি কি শিখবেন

  • জেনেরিক ক্লাস, পদ্ধতি এবং ফাংশনগুলির সাথে কীভাবে কাজ করবেন

আপনি কি করবেন

  • একটি জেনেরিক ক্লাস তৈরি করুন এবং সীমাবদ্ধতা যোগ করুন
  • in এবং out প্রকার তৈরি করুন
  • জেনেরিক ফাংশন, পদ্ধতি, এবং এক্সটেনশন ফাংশন তৈরি করুন

জেনেরিকের পরিচিতি

অনেক প্রোগ্রামিং ভাষার মতো কোটলিনেরও জেনেরিক প্রকার রয়েছে। একটি জেনেরিক টাইপ আপনাকে একটি ক্লাস জেনেরিক করতে দেয় এবং এর ফলে একটি ক্লাসকে অনেক বেশি নমনীয় করে তোলে।

কল্পনা করুন যে আপনি একটি MyList ক্লাস বাস্তবায়ন করছেন যা আইটেমগুলির একটি তালিকা ধারণ করে। জেনেরিক ছাড়া, আপনাকে প্রতিটি প্রকারের জন্য MyList এর একটি নতুন সংস্করণ বাস্তবায়ন করতে হবে: একটি Double এর জন্য, একটি String এর জন্য, একটি Fish এর জন্য। জেনেরিকের সাহায্যে, আপনি তালিকাটিকে জেনেরিক করতে পারেন, তাই এটি যেকোন ধরণের বস্তুকে ধরে রাখতে পারে। এটি টাইপটিকে একটি ওয়াইল্ডকার্ড বানানোর মতো যা অনেক ধরণের ফিট হবে।

একটি জেনেরিক টাইপ সংজ্ঞায়িত করতে, ক্লাসের নামের পরে কোণ বন্ধনী <T> এ T লিখুন। (আপনি অন্য অক্ষর বা একটি দীর্ঘ নাম ব্যবহার করতে পারেন, কিন্তু একটি জেনেরিক টাইপের কনভেনশন হল টি।)

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

আপনি T উল্লেখ করতে পারেন যেন এটি একটি স্বাভাবিক প্রকার। get() এর রিটার্ন টাইপ হল T , এবং addItem() এর প্যারামিটারটি T টাইপ। অবশ্যই, জেনেরিক তালিকাগুলি খুব দরকারী, তাই List ক্লাসটি কোটলিনে তৈরি করা হয়েছে।

ধাপ 1: একটি টাইপ অনুক্রম তৈরি করুন

এই ধাপে আপনি পরবর্তী ধাপে ব্যবহার করার জন্য কিছু ক্লাস তৈরি করুন। সাবক্লাসিং একটি আগের কোডল্যাবে আচ্ছাদিত ছিল, কিন্তু এখানে একটি সংক্ষিপ্ত পর্যালোচনা।

  1. উদাহরণটি অগোছালো রাখতে, src এর অধীনে একটি নতুন প্যাকেজ তৈরি করুন এবং এটিকে generics বলুন।
  2. জেনেরিক প্যাকেজে, একটি নতুন Aquarium.kt ফাইল তৈরি করুন। এটি আপনাকে বিরোধ ছাড়াই একই নাম ব্যবহার করে জিনিসগুলিকে পুনরায় সংজ্ঞায়িত করতে দেয়, তাই এই কোডল্যাবের জন্য আপনার বাকি কোডটি এই ফাইলে যায়৷
  3. জল সরবরাহের ধরনগুলির একটি প্রকার শ্রেণিবিন্যাস করুন। WaterSupply একটি open ক্লাস বানিয়ে শুরু করুন, যাতে এটি সাবক্লাস করা যায়।
  4. একটি বুলিয়ান var প্যারামিটার যোগ করুন, needsProcessing । এটি স্বয়ংক্রিয়ভাবে একটি পরিবর্তনযোগ্য সম্পত্তি তৈরি করে, একটি গেটার এবং সেটারের সাথে।
  5. একটি সাবক্লাস TapWater তৈরি করুন যা WaterSupply প্রসারিত করে এবং needsProcessing জন্য true পাস করে, কারণ কলের জলে অ্যাডিটিভ থাকে যা মাছের জন্য খারাপ।
  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. FishStoreWater এবং LakeWater নামে WaterSupply আরও দুটি উপশ্রেণী তৈরি করুন। FishStoreWater প্রক্রিয়াকরণের প্রয়োজন নেই, তবে LakeWater filter() পদ্ধতিতে ফিল্টার করতে হবে। ফিল্টার করার পরে, এটিকে আবার প্রসেস করার দরকার নেই, তাই filter() এ, needsProcessing = false সেট করুন।
class FishStoreWater : WaterSupply(false)

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

আপনার যদি অতিরিক্ত তথ্যের প্রয়োজন হয়, কোটলিনে উত্তরাধিকার সম্পর্কিত পূর্ববর্তী পাঠটি পর্যালোচনা করুন।

ধাপ 2: একটি জেনেরিক ক্লাস তৈরি করুন

এই ধাপে আপনি বিভিন্ন ধরনের জল সরবরাহ সমর্থন করার জন্য Aquarium শ্রেণী পরিবর্তন করেন।

  1. Aquarium.kt- এ, ক্লাসের নামের পরে বন্ধনীতে <T> সহ একটি Aquarium ক্লাস সংজ্ঞায়িত করুন।
  2. Aquarium T টাইপের একটি অপরিবর্তনীয় সম্পত্তি waterSupply যোগ করুন।
class Aquarium<T>(val waterSupply: T)
  1. genericsExample() নামে একটি ফাংশন লিখুন। এটি একটি ক্লাসের অংশ নয়, তাই এটি ফাইলের শীর্ষ স্তরে যেতে পারে, যেমন main() ফাংশন বা ক্লাস সংজ্ঞা। ফাংশনে, একটি Aquarium তৈরি করুন এবং এটিকে একটি WaterSupply সরবরাহ করুন। যেহেতু waterSupply প্যারামিটারটি জেনেরিক, তাই আপনাকে অবশ্যই এঙ্গেল ব্র্যাকেটে ধরনটি উল্লেখ করতে হবে <>
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. genericsExample() এ আপনার কোড অ্যাকোয়ারিয়ামের waterSupply অ্যাক্সেস করতে পারে। কারণ এটি TapWater টাইপের, আপনি কোনো প্রকার কাস্ট ছাড়াই addChemicalCleaners() কল করতে পারেন।
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Aquarium অবজেক্ট তৈরি করার সময়, আপনি কোণ বন্ধনী এবং তাদের মধ্যে কী আছে তা সরিয়ে ফেলতে পারেন কারণ কোটলিনের টাইপ অনুমান রয়েছে। সুতরাং আপনি যখন উদাহরণ তৈরি করবেন তখন TapWater দুবার বলার কোন কারণ নেই। ধরণটি Aquarium যুক্তি দ্বারা অনুমান করা যেতে পারে; এটি এখনও TapWater টাইপের একটি Aquarium তৈরি করবে।
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. কি ঘটছে তা দেখতে, addChemicalCleaners() কল করার আগে এবং পরে প্রিন্টের needsProcessing । নীচে সম্পন্ন ফাংশন আছে.
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

ধাপ 3: এটি আরও নির্দিষ্ট করুন

জেনেরিক মানে আপনি প্রায় যেকোনো কিছু পাস করতে পারেন এবং কখনও কখনও এটি একটি সমস্যা। এই ধাপে আপনি Aquarium শ্রেণীকে আরও সুনির্দিষ্ট করে তোলেন যে আপনি এতে কী রাখতে পারেন।

  1. genericsExample() -এ, একটি Aquarium তৈরি করুন, waterSupply জন্য একটি স্ট্রিং পাস করুন, তারপর অ্যাকোয়ারিয়ামের waterSupply সম্পত্তি প্রিন্ট করুন৷
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. আপনার প্রোগ্রাম চালান ফলাফল পর্যবেক্ষণ.
⇒ string

ফলাফল হল আপনার পাস করা স্ট্রিং, কারণ Aquarium T. তে কোনো সীমাবদ্ধতা রাখে না। String সহ যেকোনো ধরনের, পাস করা যেতে পারে।

  1. genericsExample() এ, আরেকটি Aquarium তৈরি করুন, waterSupply জন্য null পাস করুন। 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 এর অর্থ nullable Any? টাইপ, টাইপ অনুক্রমের শীর্ষে টাইপ। নিম্নলিখিতটি আপনি আগে টাইপ করেছেন তার সমতুল্য।

class Aquarium<T: Any?>(val waterSupply: T)
  1. পাসিং null মঞ্জুরি না দেওয়ার জন্য, টাইপ T এর Any স্পষ্টভাবে, অপসারণ করে তৈরি করুন ? Any পরে.
class Aquarium<T: Any>(val waterSupply: T)

এই পরিপ্রেক্ষিতে, Any একটি জেনেরিক সীমাবদ্ধতা বলা হয়। এর মানে T জন্য যেকোন প্রকার পাস করা যেতে পারে যতক্ষণ না এটি null না হয়।

  1. আপনি সত্যিই যা চান তা হল নিশ্চিত করা যে শুধুমাত্র একটি WaterSupply (বা এর একটি সাবক্লাস) T এর জন্য পাস করা যেতে পারে। আরও নির্দিষ্ট জেনেরিক সীমাবদ্ধতা সংজ্ঞায়িত করতে WaterSupply সাথে Any প্রতিস্থাপন করুন।
class Aquarium<T: WaterSupply>(val waterSupply: T)

ধাপ 4: আরও চেকিং যোগ করুন

এই ধাপে আপনি check() ফাংশন সম্পর্কে জানতে পারবেন যাতে আপনার কোড প্রত্যাশিতভাবে আচরণ করছে তা নিশ্চিত করতে সহায়তা করে। check() ফাংশনটি কোটলিনের একটি আদর্শ লাইব্রেরি ফাংশন। এটি একটি দাবী হিসাবে কাজ করে এবং একটি IllegalStateException নিক্ষেপ করবে যদি এর যুক্তিটি false বলে মূল্যায়ন করে।

  1. জল যোগ করতে Aquarium ক্লাসে একটি addWater() পদ্ধতি যোগ করুন, একটি check() সহ যা নিশ্চিত করে যে আপনাকে প্রথমে জল প্রক্রিয়া করার প্রয়োজন নেই।
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 ক্লাসটি দেখুন এবং আপনি দেখতে পাবেন যে সম্পত্তির waterSupply পাওয়ার সময় জেনেরিক টাইপটি ফেরত দেওয়া হয়। এমন কোন পদ্ধতি নেই যা একটি প্যারামিটার হিসাবে T টাইপের একটি মান নেয় (এটি কনস্ট্রাক্টরে সংজ্ঞায়িত করা ছাড়া)। কোটলিন আপনাকে ঠিক এই ক্ষেত্রে প্রকারগুলি out করতে দেয় এবং এটি কোথায় ব্যবহার করা নিরাপদ সে সম্পর্কে অতিরিক্ত তথ্য অনুমান করতে পারে। একইভাবে, আপনি জেনেরিক প্রকারের জন্য in সংজ্ঞায়িত করতে পারেন যেগুলি কেবলমাত্র পদ্ধতিতে পাস করা হয়, ফেরত দেওয়া হয় না। এটি কোড নিরাপত্তার জন্য কোটলিনকে অতিরিক্ত পরীক্ষা করার অনুমতি দেয়।

in এবং out প্রকারগুলি কোটলিনের টাইপ সিস্টেমের জন্য নির্দেশিকা। পুরো টাইপ সিস্টেমটি ব্যাখ্যা করা এই বুটক্যাম্পের সুযোগের বাইরে (এটি বেশ জড়িত); যাইহোক, কম্পাইলার এমন ধরনগুলিকে ফ্ল্যাগ করবে যেগুলি যথাযথভাবে মার্ক করা নেই in out , তাই আপনাকে সেগুলি সম্পর্কে জানতে হবে।

ধাপ 1: একটি আউট টাইপ সংজ্ঞায়িত করুন

  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 নিশ্চিত করতে পারে না যে আপনি টাইপের সাথে অনিরাপদ কিছু করছেন না।

ধাপ 2: একটি টাইপ সংজ্ঞায়িত করুন

in টাইপ out টাইপের অনুরূপ, তবে জেনেরিক প্রকারের জন্য যেগুলি শুধুমাত্র ফাংশনে পাস করা হয়, ফেরত দেওয়া হয় না। আপনি যদি একটি in ফেরত দেওয়ার চেষ্টা করেন তবে আপনি একটি কম্পাইলার ত্রুটি পাবেন। এই উদাহরণে আপনি একটি ইন্টারফেসের অংশ হিসাবে একটি in টাইপ সংজ্ঞায়িত করবেন।

  1. Aquarium.kt- এ, একটি ইন্টারফেস Cleaner সংজ্ঞায়িত করুন যা একটি জেনেরিক T নেয় যা WaterSupply জন্য সীমাবদ্ধ। যেহেতু এটি শুধুমাত্র একটি যুক্তি হিসাবে ব্যবহার করা হয় clean() , আপনি এটি একটি in প্যারামিটার করতে পারেন।
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. Cleaner ইন্টারফেস ব্যবহার করতে, একটি ক্লাস TapWaterCleaner তৈরি করুন যা রাসায়নিক যোগ করে TapWater পরিষ্কারের জন্য Cleaner প্রয়োগ করে।
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. Aquarium ক্লাসে, T টাইপের Cleaner নিতে addWater() আপডেট করুন এবং এটি যোগ করার আগে জল পরিষ্কার করুন।
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. একটি TapWaterCleaner , TapWater সহ একটি Aquarium তৈরি করতে genericsExample() উদাহরণ কোড আপডেট করুন এবং তারপর ক্লিনার ব্যবহার করে কিছু জল যোগ করুন। এটি প্রয়োজন অনুযায়ী ক্লিনার ব্যবহার করবে।
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

আপনার কোড নিরাপদে জেনেরিক ব্যবহার করছে তা নিশ্চিত করতে Kotlin in এবং out ধরনের তথ্য ব্যবহার করবে। Out এবং in মনে রাখা সহজ: out প্রকারগুলি রিটার্ন মান হিসাবে বাইরের দিকে পাস করা যেতে পারে, in টাইপগুলি আর্গুমেন্ট হিসাবে ভিতরের দিকে পাস করা যেতে পারে।

আপনি যদি টাইপ এবং আউট টাইপের সমস্যা সমাধানের জন্য আরও খনন করতে চান তবে ডকুমেন্টেশনটি সেগুলিকে গভীরভাবে কভার করে।

এই টাস্কে আপনি জেনেরিক ফাংশন এবং কখন সেগুলি ব্যবহার করবেন তা শিখবেন। সাধারণত, একটি জেনেরিক ফাংশন তৈরি করা একটি ভাল ধারণা যখনই ফাংশনটি একটি জেনেরিক টাইপের একটি ক্লাসের আর্গুমেন্ট নেয়।

ধাপ 1: একটি জেনেরিক ফাংশন তৈরি করুন

  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. ফাংশনটিকে জেনেরিক করার জন্য, একটি জেনেরিক টাইপ T এবং যেকোনো সীমাবদ্ধতার সাথে কীওয়ার্ড fun পরে কোণ বন্ধনী রাখুন, এই ক্ষেত্রে, WaterSupplyWaterSupply পরিবর্তে T দ্বারা সীমাবদ্ধ Aquarium পরিবর্তন করুন।
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 থেকে টাইপের অনুমানের কারণে, টাইপের প্রয়োজন নেই, তাই এটি সরিয়ে ফেলুন। আপনার প্রোগ্রাম চালান এবং আউটপুট পর্যবেক্ষণ.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

ধাপ 2: রিফাইড টাইপ দিয়ে একটি জেনেরিক পদ্ধতি তৈরি করুন

আপনি পদ্ধতির জন্যও জেনেরিক ফাংশন ব্যবহার করতে পারেন, এমনকি তাদের নিজস্ব জেনেরিক ধরনের ক্লাসেও। এই ধাপে, আপনি Aquarium একটি জেনেরিক পদ্ধতি যোগ করুন যা এটিতে WaterSupply ধরন আছে কিনা তা পরীক্ষা করে।

  1. Aquarium ক্লাসে, একটি পদ্ধতি ঘোষণা করুন, hasWaterSupplyOfType() যেটি একটি জেনেরিক প্যারামিটার R ( T ইতিমধ্যেই ব্যবহৃত হয়েছে) নেয় WaterSupply সীমাবদ্ধ , এবং যদি waterSupply R টাইপের হয় তবে true ফেরত দেয়। এটি আপনি আগে ঘোষিত ফাংশন মত, কিন্তু Aquarium ক্লাস ভিতরে.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. লক্ষ্য করুন যে চূড়ান্ত R লাল রঙে আন্ডারলাইন করা হয়েছে। এররটি কী তা দেখতে পয়েন্টারটি ধরে রাখুন।
  2. একটি is করার জন্য, আপনাকে Kotlin কে বলতে হবে যে টাইপটি reified বা বাস্তব, এবং ফাংশনে ব্যবহার করা যেতে পারে। এটি করার জন্য, fun কীওয়ার্ডের সামনে inline রাখুন এবং জেনেরিক টাইপ R এর সামনে reified
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

একবার একটি টাইপ রিফাইড হয়ে গেলে, আপনি এটিকে সাধারণ টাইপের মতো ব্যবহার করতে পারেন-কারণ ইনলাইন করার পরে এটি একটি বাস্তব প্রকার। তার মানে আপনি করতে পারেন টাইপ ব্যবহার করে is .

আপনি যদি এখানে reified ব্যবহার না করেন, তাহলে টাইপটি "বাস্তব" হবে না যাতে কোটলিন is অনুমতি দেয়। এর কারণ হল নন-রিফাইড টাইপগুলি শুধুমাত্র কম্পাইলের সময় উপলব্ধ, এবং আপনার প্রোগ্রাম দ্বারা রানটাইমে ব্যবহার করা যাবে না। এটি পরবর্তী বিভাগে আরও আলোচনা করা হয়েছে।

  1. টাইপ হিসাবে TapWater পাস করুন। জেনেরিক ফাংশন কল করার মতো, ফাংশনের নামের পরে টাইপ সহ কোণ বন্ধনী ব্যবহার করে জেনেরিক পদ্ধতিগুলিকে কল করুন। আপনার প্রোগ্রাম চালান এবং ফলাফল পর্যবেক্ষণ.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

ধাপ 3: এক্সটেনশন ফাংশন করুন

আপনি নিয়মিত ফাংশন এবং এক্সটেনশন ফাংশনগুলির জন্যও রিফাইড টাইপ ব্যবহার করতে পারেন।

  1. Aquarium ক্লাসের বাইরে, WaterSupplyisOfType() নামে একটি এক্সটেনশন ফাংশন সংজ্ঞায়িত করুন যা পরীক্ষা করে যে পাস করা 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 মূল API এর অংশ নয়।
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 করতে হবে, কারণ কোটলিনকে রানটাইমে সেগুলি সম্পর্কে জানতে হবে, শুধু কম্পাইল সময় নয়।

সমস্ত জেনেরিক প্রকারগুলি শুধুমাত্র কোটলিন দ্বারা কম্পাইলের সময় ব্যবহৃত হয়। এটি কম্পাইলারকে নিশ্চিত করতে দেয় যে আপনি সবকিছু নিরাপদে করছেন। রানটাইম দ্বারা সমস্ত জেনেরিক প্রকার মুছে ফেলা হয়, তাই একটি মুছে ফেলা টাইপ চেক করার বিষয়ে আগের ত্রুটি বার্তা।

এটি দেখা যাচ্ছে যে কম্পাইলার রানটাইম পর্যন্ত জেনেরিক প্রকারগুলি না রেখে সঠিক কোড তৈরি করতে পারে। কিন্তু এর মানে এই যে কখনও কখনও আপনি কিছু করেন, is জেনেরিক ধরনের চেক, যে কম্পাইলার সমর্থন করতে পারে না। এই কারণেই কোটলিন রিফাইড বা বাস্তব প্রকার যোগ করেছে।

আপনি কোটলিন ডকুমেন্টেশনে রিফাইড প্রকার এবং টাইপ ইরেজার সম্পর্কে আরও পড়তে পারেন।

এই পাঠটি জেনেরিকের উপর দৃষ্টি নিবদ্ধ করে, যা কোডকে আরও নমনীয় এবং পুনরায় ব্যবহার করা সহজ করার জন্য গুরুত্বপূর্ণ।

  • কোড আরও নমনীয় করতে জেনেরিক ক্লাস তৈরি করুন।
  • জেনেরিকের সাথে ব্যবহৃত প্রকারগুলিকে সীমাবদ্ধ করতে জেনেরিক সীমাবদ্ধতা যুক্ত করুন।
  • ক্লাসে পাস করা বা প্রত্যাবর্তন করা প্রকারগুলিকে সীমাবদ্ধ করতে আরও ভাল টাইপ চেকিং প্রদান করতে জেনেরিক সহ in এবং out প্রকারগুলি ব্যবহার করুন৷
  • জেনেরিক প্রকারের সাথে কাজ করার জন্য জেনেরিক ফাংশন এবং পদ্ধতি তৈরি করুন। যেমন:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • একটি ক্লাসে নন-কোর কার্যকারিতা যোগ করতে জেনেরিক এক্সটেনশন ফাংশন ব্যবহার করুন।
  • টাইপ ইরেজারের কারণে রিফাইড টাইপগুলি কখনও কখনও প্রয়োজনীয়। Reified প্রকারগুলি, জেনেরিক প্রকারের বিপরীতে, রানটাইম পর্যন্ত টিকে থাকে।
  • আপনার কোড প্রত্যাশিতভাবে চলছে তা যাচাই করতে check() ফাংশনটি ব্যবহার করুন। যেমন:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

কোটলিন ডকুমেন্টেশন

আপনি যদি এই কোর্সে কোনো বিষয়ে আরও তথ্য চান, বা আপনি যদি আটকে যান, https://kotlinlang.org আপনার সেরা শুরুর পয়েন্ট।

কোটলিন টিউটোরিয়াল

https://try.kotlinlang.org ওয়েবসাইটটিতে Kotlin Koans নামে সমৃদ্ধ টিউটোরিয়াল রয়েছে, একটি ওয়েব-ভিত্তিক দোভাষী , এবং উদাহরণ সহ রেফারেন্স ডকুমেন্টেশনের একটি সম্পূর্ণ সেট।

উদ্যতা কোর্স

এই বিষয়ে Udacity কোর্সটি দেখতে, প্রোগ্রামারদের জন্য Kotlin Bootcamp দেখুন।

ইন্টেলিজ আইডিয়া

IntelliJ IDEA-এর জন্য ডকুমেন্টেশন জেটব্রেইন্স ওয়েবসাইটে পাওয়া যাবে।

এই বিভাগে একজন প্রশিক্ষকের নেতৃত্বে একটি কোর্সের অংশ হিসাবে এই কোডল্যাবের মাধ্যমে কাজ করা শিক্ষার্থীদের জন্য সম্ভাব্য হোমওয়ার্ক অ্যাসাইনমেন্ট তালিকাভুক্ত করা হয়েছে। নিম্নলিখিতগুলি করা প্রশিক্ষকের উপর নির্ভর করে:

  • প্রয়োজনে হোমওয়ার্ক বরাদ্দ করুন।
  • শিক্ষার্থীদের সাথে যোগাযোগ করুন কিভাবে হোমওয়ার্ক অ্যাসাইনমেন্ট জমা দিতে হয়।
  • হোমওয়ার্ক অ্যাসাইনমেন্ট গ্রেড.

প্রশিক্ষকরা এই পরামর্শগুলি যতটা কম বা যতটা চান ততটা ব্যবহার করতে পারেন, এবং তাদের উপযুক্ত মনে করে অন্য কোনও হোমওয়ার্ক বরাদ্দ করতে নির্দ্বিধায় করা উচিত।

আপনি যদি নিজে থেকে এই কোডল্যাবের মাধ্যমে কাজ করে থাকেন, তাহলে আপনার জ্ঞান পরীক্ষা করার জন্য এই হোমওয়ার্ক অ্যাসাইনমেন্টগুলিকে নির্দ্বিধায় ব্যবহার করুন৷

এই প্রশ্নগুলোর উত্তর দাও

প্রশ্ন 1

নিচের কোনটি জেনেরিক ধরনের নামকরণের নিয়ম?

<Gen>

<Generic>

<T>

<X>

প্রশ্ন 2

একটি জেনেরিক ধরনের জন্য অনুমোদিত ধরনের উপর একটি সীমাবদ্ধতা বলা হয়:

▢ একটি সাধারণ সীমাবদ্ধতা

▢ একটি সাধারণ সীমাবদ্ধতা

▢ দ্ব্যর্থতা নিরসন

▢ একটি সাধারণ প্রকারের সীমা

প্রশ্ন 3

রিফাইড মানে:

▢ একটি বস্তুর বাস্তব কার্য সম্পাদনের প্রভাব গণনা করা হয়েছে।

▢ ক্লাসে একটি সীমাবদ্ধ প্রবেশ সূচক সেট করা হয়েছে।

▢ জেনেরিক টাইপ প্যারামিটারটিকে একটি বাস্তব টাইপ করা হয়েছে।

▢ একটি দূরবর্তী ত্রুটি নির্দেশক ট্রিগার করা হয়েছে৷

পরবর্তী পাঠে এগিয়ে যান: 6. কার্যকরী ম্যানিপুলেশন

অন্যান্য কোডল্যাবগুলির লিঙ্ক সহ কোর্সের একটি ওভারভিউয়ের জন্য, "প্রোগ্রামারদের জন্য কোটলিন বুটক্যাম্প: কোর্সে স্বাগতম" দেখুন।