Generic در کاتلین
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 3 دقیقه

Generic در کاتلین

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

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

۱. Type در مقابل Class و SubType

  • Type خواصی را که مجموعه‌ای از اشیاء ممکن است به اشتراک بگذارند، توصیف می‌کند
  • Class فقط پیاده‌سازی آن نوع(type) است
  • SubType حداقل باید همان محدوده از نوع (type) را قبول کند همان‌طور که superType آن مشخص کرده است 
  • SubType باید حداکثر همان محدوه از نوع (type) را برگرداند همان‌طور که superType آن مشخص می‌کند

۲. Variance

Variance به چگونگی زیرمجموعه کردن نوع‌های بسیار پیچیده، مربوط به زیرمجموعه در بین اجزا آن‌ها است.

قرارداد: E = element | T = type | K = key | V = value

data class Course(val name: String)

class OddList<T>(val list: List<T>) {
    fun oddItems(): List<T> {
        return list.filterIndexed { index, _ -> index % 2 == 1 }
    }
}

fun main() {
 val listOfStrings = listOf("Kotlin", "Java", "C#")
    val resultOfStrings: OddList<String> = OddList(listOfStrings)
    println(resultOfStrings.oddItems())

    val listOfInts = listOf(1, 7, 8, 9, 12, 45)
    val resultOfInts = OddList(listOfInts)
    println(resultOfInts.oddItems())

    val courses = listOf(
        Course("Kotlin"),
        Course("Java"),
        Course("C#"),
        Course("PHP"),
        Course("C++")
    )
    var resultCourses = OddList(courses).oddItems()
    println(resultCourses)
}

۳. Covariance

  • اگر C<T> یک نوع generic با پارامتر T و U زیر مجموعه T باشد، بنابراین C<U> زیر مجموعه C<T> است
  • مثال: List<Int> یک زیر مجموعه از List<Number> است زیرا Int زیر مجموعه Number است
  • به نوع‌هایی که تولید کننده یا منبع T هستند اعمال می‌شود
  • T فقط در موقعیت "out" ظاهر می‌شود، یعنی نوع بازگشت یک تابع

class CovarianceSample<T>

fun main() {
  val firstSample: CovarianceSample<Any> = CovarianceSample<Int>()  // Error: Type mismatch
  val secondSample: CovarianceSample<out Any> = CovarianceSample<String>() // OK , String is a subtype of Any
  val thirdSample: CovarianceSample<out String> = CovarianceSample<Any>() // Error: Type mismatch
}

۴. Contravariance

  • اگر C<T> یک نوع generic با پارامتر T و U زیر مجموعه‌ای از T باشد، بنابراین C<T> زیر مجموعه C<U> است
  • U زیرمجموعه‌ای از T است => C<T> زیر مجموعه‌ای از C<U> است
  • مثال: Function1<Number, Int> زیر مجموعه‌ای از Function1<Int, Int> است زیرا Int زیرمجموعه‌ای از Number است
  • به نوع‌هایی که مصرف کننده T هستند اعمال می‌شود
  • T فقط در موقعیت "in" ظاهر می‌شود، یعنی نوع آرگومان تابع

open class Vehicle
class Bicycle : Vehicle()
class Container<in T>

fun main() {
  var containerBicycle: Container<Bicycle> = Container<Vehicle>() // OK
  var containerVehicle: Container<Vehicle> = Container<Bicycle>() // Error: Type mismatch
}

۵. Invariance

  • اگر C<T> زیر مجموعه C<U> باشد، بنابراین T=U است
  • مثال: Array<T> یک ثابت در T است
  • T در هر دو "in position" و "out position" ظاهر می‌شود
  • نوع(type) هم تولید کننده و هم مصرف کننده T است
  • یادآوری: لامبدا در نوع آرگومان Contra-variant است و در نوع خروجی covariant است

6. Type Projection

  • Type projection نوعی است که به منظور بدست آوردن مشخصات variance با استفاده از use-site variance به چند روش محدود شده است
class KotlinConsumer<in T> {
    fun toString(value: T): String {
        return value.toString()
    }
}

fun main() {
    val inValue: KotlinConsumer<Number> = KotlinConsumer()
    val copyOfInValue: KotlinConsumer<Int> = inValue
    if (copyOfInValue is KotlinConsumer<Int>) {
        println(copyOfInValue.toString())
    }
}
class KotlinProducer<out T>(val value: T) {
    init {
        println(value)
    }
}

fun main() {
     val outValue = KotlinProducer("Just a string")
    val copyOfOutValue: KotlinProducer<Any> = outValue
    println(copyOfOutValue.value.toString())
}

7. Star Projection

  • آنها وقتی که چیزی از نوع آرگومان نمی‌دانیم ولی نیاز داریم که از آن به روش ایمن استفاده کنیم مفید هستند
  • راه مطمئن در اینجا تعریف projection از نوع generic است، که هر لحظه بَتن آن نوع generic، زیر مجموعه آن projection است
  • به جای استفاده از C<in Nothing> یا C<out Any?>، می‌توانید فقط از C<*> استفاده کنید. همان رابط کاربری موثر را با دو رویکرد دیگر تولید می‌کند
  • بنابراین ما سه راه داریم که می‌توانیم هر نوعی از generic را بپذیریم:
    • In-projection - <in Nothing>
    • In-projection - <out Any?>
    • Star-projection - <*>
val languages = arrayOf("Kotlin", "Java", "Generics", 1)
printArray(languages)

۸. پارامترهای type erasure و reified type

  • جاوا محدودیت‌هایی در مورد نوع‌های reified در نظر گرفته شده دارد، یعنی آن‌ها در زمان اجرا کاملا در دسترس هستند
  • بررسی‌های type safty که کاتلین برای موارد استفاده generic انجام می‌دهد فقط در زمان کامپایل انجام می‌شود. در زمان اجرا، نمونه‌های نوع generic هیچ‌گونه اطلاعاتی درباره نوع واقعی آرگومان‌ها در دست ندارند
  • محدودیت‌های type فقط در زمان کامپایل و زمان دور انداختن اطلاعات نوع عنصر در زمان اجرا اعمال می‌شود
  • گفته می‎شود که نوع اطلاعات پاک می‌شوند
  • در مورد پارامترهای نوع reified در کاتلین، می‌توانیم type‌ها را مقایسه کنیم و اشیاء کلاس را بدست آوریم

پارامترهای نوع reified:

  • فقط با توابع کار می‌کنند(و یا خصوصیات  extension که تابع get() دارند)
  • با توابعی که به صورت inline اعلام شده اند کار می‌کنند

مزایای استفاده از پارامترهای نوع reified:

  • بررسی type
  • Cast کردن بدون هشدارهای بررسی نشده
  • اشیاء کلاس را با افزودن ::class.java به نام پارامتر اختصاص دهید

مثال: val a = T :: class.java

// single param
inline fun <reified T> Any.isInstanceOf(): Boolean = this is T

fun main() {
  val isStringAString = "String".isInstanceOf<String>()
  val isIntAString = 1.isInstanceOf<String>()
}

// multiple params
inline fun <reified T, reified U> haveSameType(first: T, second: U) = 
        first is U && second is T

// extension properties, but the type parameter is used as the receiver type
inline val <reified T> T.theClass
    get() = T::class.java

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
3 از 1 رای

/@pouryasharifi78
پوریا شریفی
توسعه‌دهنده‌ی اندروید

ابتدا که با برنامه‌نویسی آشنا شدم به سمت php و طراحی وب رفتم، بعد از اون به توسعه‌ی اندروید علاقه‌مند شدم و تقریبا ۲ سال است که مشغول به برنامه‌نویسی اندروید هستم، همچنین عاشق یادگیری چیزهای جدید هستم.

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

پوریا شریفی

توسعه‌دهنده‌ی اندروید