Kotlin Bootcamp for Programmers 5.2: Generics

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

مقدمه

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

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

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

  1. در کلاس Aquarium ، T: WaterSupply به یک نوع out تغییر دهید.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. در همان فایل، خارج از کلاس، یک تابع addItemTo() که انتظار یک Aquarium of WaterSupply دارد، اعلام کنید.
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() با خطا مواجه می‌شود، زیرا کاتلین نمی‌تواند اطمینان حاصل کند که شما هیچ کاری ناامن با نوع انجام نمی‌دهید.

مرحله 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)
}

کاتلین از اطلاعات نوع 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. برای عمومی کردن تابع، براکت‌های زاویه‌ای را بعد از کلمه کلیدی 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 یا واقعی است و می تواند در تابع استفاده شود. برای انجام این کار، در مقابل کلمه کلیدی fun قرار دهید و در مقابل نوع عمومی R reified inline قرار دهید.
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 یا واقعی را اضافه کرد.

می توانید اطلاعات بیشتری در مورد انواع 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. دستکاری عملکردی

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