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
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید