Kotlin Bootcamp for Programmers 5.2: Generics

این Codelab بخشی از دوره Kotlin Bootcamp برای برنامه نویسان است . اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. بسته به دانش خود، ممکن است بتوانید برخی از بخش ها را مرور کنید. این دوره برای برنامه نویسانی است که زبان شی گرا را می دانند و می خواهند Kotlin را یاد بگیرند.

مقدمه

در این Codelab با کلاس ها، توابع و متدهای عمومی و نحوه کار آنها در Kotlin آشنا می شوید.

به جای ساختن یک برنامه نمونه واحد، درس‌های این دوره برای ایجاد دانش شما طراحی شده‌اند، اما نیمه مستقل از یکدیگر باشند تا بتوانید بخش‌هایی را که با آن‌ها آشنا هستید، مرور کنید. برای گره زدن آنها به یکدیگر، بسیاری از نمونه ها از تم آکواریوم استفاده می کنند. و اگر می خواهید داستان کامل آکواریوم را ببینید، دوره Kotlin Bootcamp for Programmers Udacity را بررسی کنید.

آنچه از قبل باید بدانید

  • نحو توابع، کلاس ها و متدهای کاتلین
  • نحوه ایجاد یک کلاس جدید در IntelliJ IDEA و اجرای یک برنامه

چیزی که یاد خواهید گرفت

  • نحوه کار با کلاس ها، متدها و توابع عمومی

کاری که خواهی کرد

  • یک کلاس عمومی ایجاد کنید و محدودیت ها را اضافه کنید
  • انواع in و out ایجاد کنید
  • توابع، متدها و توابع توسعه عمومی را ایجاد کنید

مقدمه ای بر ژنریک

کاتلین مانند بسیاری از زبان های برنامه نویسی دارای انواع عمومی است. یک نوع عمومی به شما امکان می دهد یک کلاس را عمومی کنید و در نتیجه یک کلاس را بسیار انعطاف پذیرتر کنید.

تصور کنید در حال پیاده سازی یک کلاس 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 است. البته لیست های عمومی بسیار مفید هستند، بنابراین کلاس List در Kotlin تعبیه شده است.

مرحله 1: یک سلسله مراتب نوع ایجاد کنید

در این مرحله شما چند کلاس ایجاد می کنید تا در مرحله بعد از آنها استفاده کنید. طبقه بندی فرعی در یک کد آزمایشگاه قبلی پوشش داده شده بود، اما در اینجا یک بررسی مختصر وجود دارد.

  1. برای بی نظم نگه داشتن مثال، یک بسته جدید در زیر src ایجاد کنید و آن را generics .
  2. در بسته ژنریک ، یک فایل Aquarium.kt جدید ایجاد کنید. این به شما امکان می‌دهد با استفاده از نام‌های یکسان و بدون تداخل، چیزها را دوباره تعریف کنید، بنابراین بقیه کد شما برای این کد لبه وارد این فایل می‌شود.
  3. سلسله مراتبی از انواع تامین آب ایجاد کنید. با تبدیل کردن WaterSupply به یک کلاس open شروع کنید تا بتوان آن را زیر کلاس‌بندی کرد.
  4. یک پارامتر var boolean اضافه کنید، 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. دو زیر کلاس دیگر از WaterSupply به نام های FishStoreWater و LakeWater کنید. FishStoreWater نیازی به پردازش ندارد، اما LakeWater باید با روش filter() شود. پس از فیلتر کردن، نیازی به پردازش مجدد نیست، بنابراین در filter() needsProcessing = false را تنظیم کنید.
class FishStoreWater : WaterSupply(false)

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

اگر به اطلاعات بیشتری نیاز دارید، درس قبلی در مورد وراثت در کاتلین را مرور کنید.

مرحله 2: یک کلاس عمومی بسازید

در این مرحله کلاس Aquarium را برای پشتیبانی از انواع مختلف منابع آب تغییر می دهید.

  1. در Aquarium.kt یک کلاس Aquarium را با <T> در پرانتز بعد از نام کلاس تعریف کنید.
  2. اضافه کردن یک ویژگی تغییر ناپذیر waterSupply از نوع T به Aquarium .
class Aquarium<T>(val waterSupply: T)
  1. تابعی به نام genericsExample() بنویسید. این بخشی از یک کلاس نیست، بنابراین می تواند در سطح بالای فایل مانند تابع main() یا تعاریف کلاس قرار گیرد. در عملکرد، یک Aquarium بسازید و آن را به عنوان WaterSupply ارسال کنید. از آنجایی که پارامتر waterSupply عمومی است، باید نوع آن را در براکت های زاویه ای <> مشخص کنید.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. در genericsExample() کد شما می تواند به waterSupply آکواریوم دسترسی داشته باشد. از آنجایی که از نوع TapWater است، می توانید addChemicalCleaners() را بدون هیچ نوع cast فراخوانی کنید.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. هنگام ایجاد آبجکت Aquarium ، می‌توانید براکت‌های زاویه و آنچه را که بین آن‌ها وجود دارد حذف کنید، زیرا کاتلین استنتاج نوع دارد. بنابراین دلیلی وجود ندارد که هنگام ایجاد نمونه، دو بار بگویید TapWater . نوع را می توان با استدلال به Aquarium استنباط کرد. همچنان یک Aquarium از نوع TapWater می سازد.
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. یک تابع main() برای فراخوانی genericsExample() اضافه کنید، سپس برنامه خود را اجرا کنید و نتیجه را مشاهده کنید.
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 null است، "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. برای اینکه اجازه T null Any شود، با حذف ? بعد از Any .
class Aquarium<T: Any>(val waterSupply: T)

در این زمینه، Any یک محدودیت عمومی نامیده می شود. این بدان معناست که هر نوع تا زمانی که null نباشد، می تواند برای T ارسال شود.

  1. آنچه واقعاً می خواهید این است که مطمئن شوید فقط یک WaterSupply (یا یکی از زیر کلاس های آن) می تواند برای T ارسال شود. برای تعریف یک محدودیت عمومی خاص تر، Any را با WaterSupply جایگزین کنید.
class Aquarium<T: WaterSupply>(val waterSupply: T)

مرحله 4: بررسی بیشتر را اضافه کنید

در این مرحله با تابع check() آشنا می شوید تا مطمئن شوید کد شما مطابق انتظار عمل می کند. تابع check() یک تابع کتابخانه استاندارد در Kotlin است. این به عنوان یک ادعا عمل می کند و اگر آرگومان آن false ارزیابی شود، یک IllegalStateException می کند.

  1. یک addWater() را به کلاس Aquarium اضافه کنید تا آب اضافه کنید، با یک 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() کدی را برای ساخت Aquarium با LakeWater اضافه کنید و سپس مقداری آب به آن اضافه کنید.
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 را به عنوان پارامتر بگیرد (به جز تعریف آن در سازنده). Kotlin به شما امکان می دهد انواع را دقیقاً برای این مورد تعریف out ، و می تواند اطلاعات اضافی در مورد محل استفاده از انواع بی خطر استنتاج کند. به طور مشابه، می‌توانید انواعی را برای انواع عمومی تعریف in که فقط به متدها منتقل می‌شوند و برگردانده نمی‌شوند. این به Kotlin اجازه می دهد تا بررسی های بیشتری را برای ایمنی کد انجام دهد.

انواع ورودی و out دستورالعمل in برای سیستم نوع کاتلین هستند. توضیح کل سیستم نوع خارج از محدوده این بوت کمپ است (بسیار درگیر است). با این حال، کامپایلر انواعی را که به طور مناسب به in و out علامت گذاری نشده اند، پرچم گذاری می کند، بنابراین باید در مورد آنها بدانید.

مرحله 1: یک نوع خارج را تعریف کنید

  1. در کلاس Aquarium ، T: WaterSupply را به یک نوع out تغییر دهید.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. در همان فایل، خارج از کلاس، تابع addItemTo() را که انتظار یک Aquarium WaterSupply را دارد، اعلام کنید.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. از addItemTo() genericsExample() کنید و برنامه خود را اجرا کنید.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

Kotlin می‌تواند اطمینان حاصل کند که addItemTo() هیچ کاری از نوع ناامن با WaterSupply عمومی انجام نمی‌دهد، زیرا به‌عنوان یک نوع out اعلام شده است.

  1. اگر کلمه کلیدی out را حذف کنید، کامپایلر هنگام فراخوانی addItemTo() با خطا مواجه می‌شود، زیرا کاتلین نمی‌تواند اطمینان حاصل کند که شما هیچ کاری ناامن با نوع انجام نمی‌دهید.

مرحله 2: نوع in را تعریف کنید

نوع in شبیه به نوع out است، اما برای انواع عمومی که فقط به توابع منتقل می‌شوند، برگردانده نمی‌شوند. اگر سعی کنید یک نوع in را برگردانید، یک خطای کامپایلر دریافت خواهید کرد. در این مثال شما یک نوع in را به عنوان بخشی از یک رابط تعریف می کنید.

  1. در Aquarium.kt ، یک Interface Cleaner را تعریف کنید که یک T عمومی را می گیرد که به WaterSupply محدود شده است. از آنجایی که فقط به عنوان یک آرگومان برای 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() را به روز کنید تا یک Cleaner از نوع T بگیرد و قبل از اضافه کردن آب آن را تمیز کنید.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. برای ایجاد یک TapWaterCleaner ، یک Aquarium با TapWater ، کد نمونه genericsExample() را به روز کنید و سپس با استفاده از پاک کننده مقداری آب اضافه کنید. در صورت نیاز از پاک کننده استفاده می کند.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

کاتلین از اطلاعات نوع ورودی و out استفاده in کند تا مطمئن شود کد شما به طور ایمن از ژنریک استفاده می کند. یادآوری کردن 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. برای عمومی کردن تابع، براکت‌های زاویه‌ای را بعد از کلمه کلیدی fun با نوع T عمومی و هرگونه محدودیت، در این مورد WaterSupply قرار دهید. Aquarium را تغییر دهید تا توسط T به جای WaterSupply شود.
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: یک روش عمومی با نوع reified ایجاد کنید

شما می توانید از توابع عمومی برای متدها نیز استفاده کنید، حتی در کلاس هایی که نوع عمومی خود را دارند. در این مرحله، یک روش عمومی به 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 یا واقعی است و می تواند در تابع استفاده شود. برای انجام این کار، در مقابل کلمه کلیدی inline قرار fun و در reified نوع عمومی R .
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

هنگامی که یک نوع reified شد، می توانید از آن مانند یک نوع معمولی استفاده کنید - زیرا بعد از خط کشی یک نوع واقعی است. این بدان معنی است که شما می توانید با استفاده از is بررسی کنید.

اگر در اینجا از reified استفاده نمی کنید، نوع آن به اندازه کافی "واقعی" نخواهد بود که Kotlin بتواند چک is را مجاز کند. دلیلش این است که انواع غیر اصلاح‌شده فقط در زمان کامپایل در دسترس هستند و در زمان اجرا توسط برنامه شما قابل استفاده نیستند. در بخش بعدی بیشتر به این موضوع پرداخته می شود.

  1. TapWater را به عنوان نوع عبور دهید. مانند فراخوانی توابع عمومی، روش‌های عمومی را با استفاده از براکت‌های زاویه با نوع بعد از نام تابع فراخوانی کنید. برنامه خود را اجرا کنید و نتیجه را مشاهده کنید.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

مرحله 3: ایجاد توابع افزونه

می توانید از انواع reified برای توابع معمولی و توابع افزونه نیز استفاده کنید.

  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 باشد. استفاده از سینتکس ستاره پرتاب یک راه راحت برای تعیین انواع تطابق است. و هنگامی که از یک ستاره پرتاب استفاده می کنید، کاتلین مطمئن می شود که شما نیز هیچ کاری ناامن انجام نمی دهید.

  1. برای استفاده از پرتاب ستاره، <*> را بعد از Aquarium قرار دهید. hasWaterSupplyOfType() را به عنوان یک تابع افزونه منتقل کنید، زیرا در واقع بخشی از API اصلی 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 استفاده می شوند. این به کامپایلر امکان می دهد مطمئن شود که همه چیز را با خیال راحت انجام می دهید. در زمان اجرا همه انواع عمومی پاک می شوند، از این رو پیام خطای قبلی در مورد بررسی یک نوع پاک شده است.

به نظر می رسد که کامپایلر می تواند کد صحیح را بدون حفظ انواع عمومی تا زمان اجرا ایجاد کند. اما به این معنی است که گاهی اوقات کاری انجام می دهید is مانند بررسی انواع عمومی، که کامپایلر نمی تواند آن را پشتیبانی کند. به همین دلیل است که کاتلین انواع reified یا واقعی را اضافه کرد.

می توانید اطلاعات بیشتری در مورد انواع اصلاح شده و پاک کردن نوع در اسناد Kotlin بخوانید.

این درس بر روی ژنریک تمرکز داشت که برای انعطاف‌پذیری بیشتر کد و استفاده مجدد آسان‌تر از آن مهم است.

  • کلاس های عمومی ایجاد کنید تا کد را انعطاف پذیرتر کنید.
  • برای محدود کردن انواع مورد استفاده در ژنریک، محدودیت‌های عمومی اضافه کنید.
  • in انواع ورودی و out با ژنریک استفاده کنید تا بررسی نوع بهتری را برای محدود کردن انواع ارسال یا بازگرداندن از کلاس‌ها انجام دهید.
  • توابع و روش های عمومی را برای کار با انواع عمومی ایجاد کنید. مثلا:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • از توابع پسوند عمومی برای افزودن عملکردهای غیر اصلی به یک کلاس استفاده کنید.
  • انواع Reified گاهی اوقات به دلیل پاک کردن نوع ضروری هستند. انواع Reified، بر خلاف انواع عمومی، تا زمان اجرا باقی می مانند.
  • از تابع check() برای تأیید اینکه کد شما مطابق انتظار اجرا می شود استفاده کنید. مثلا:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

مستندات کاتلین

اگر در مورد هر موضوعی در این دوره اطلاعات بیشتری می خواهید، یا اگر گیر کرده اید، https://kotlinlang.org بهترین نقطه شروع شما است.

آموزش های کاتلین

وب‌سایت https://try.kotlinlang.org شامل آموزش‌های غنی به نام Kotlin Koans، یک مترجم مبتنی بر وب و مجموعه کاملی از مستندات مرجع با مثال است.

دوره جسارت

برای مشاهده دوره Udacity در مورد این موضوع، به Kotlin Bootcamp for Programmers مراجعه کنید.

ایده IntelliJ

اسناد IntelliJ IDEA را می توان در وب سایت JetBrains یافت.

این بخش، تکالیف احتمالی را برای دانش‌آموزانی که در این آزمایشگاه کد به عنوان بخشی از دوره‌ای که توسط یک مربی هدایت می‌شود، فهرست می‌کند. این وظیفه مربی است که موارد زیر را انجام دهد:

  • در صورت نیاز تکالیف را تعیین کنید.
  • نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
  • تکالیف را نمره دهید.

مربیان می‌توانند از این پیشنهادات به اندازه‌ای که می‌خواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر می‌کنند مناسب است به آنها اختصاص دهند.

اگر به تنهایی بر روی این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.

به این سوالات پاسخ دهید

سوال 1

کدام یک از موارد زیر برای نامگذاری یک نوع عمومی مناسب است؟

<Gen>

<Generic>

<T>

<X>

سوال 2

محدودیت در انواع مجاز برای یک نوع عمومی نامیده می شود:

▢ یک محدودیت عمومی

▢ یک محدودیت عمومی

▢ ابهام زدایی

▢ محدودیت نوع عمومی

سوال 3

ریفید یعنی:

▢ تاثیر اجرای واقعی یک شی محاسبه شده است.

▢ یک شاخص ورودی محدود روی کلاس تنظیم شده است.

▢ پارامتر نوع عمومی به یک نوع واقعی تبدیل شده است.

▢ نشانگر خطای راه دور فعال شده است.

به درس بعدی بروید: 6. دستکاری عملکردی

برای یک نمای کلی از دوره، از جمله پیوندهایی به دیگر کد لبه ها، به «کوتلین بوت کمپ برای برنامه نویسان: به دوره خوش آمدید» مراجعه کنید.