16-bo‘lim

Generiklar

Generik funksiya va sinflar, cheklovlar, in va out variantligi, reified turlar hamda yulduzcha proyeksiyasi.

🕑 13 daqiqa o‘qish 📄 564 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Generik funksiya
  2. Generik sinf
  3. Cheklovlar
  4. Bir nechta cheklov
  5. null bo'lmasligi
  6. Variantlik: out
  7. Variantlik: in
  8. reified
  9. Yulduzcha proyeksiyasi
  10. Amaliy misol
  11. Xulosa

Generik - turni keyinroq aytishga imkon beradigan yozuv. List<String> va List<Int> - bitta sinfning ikki ko'rinishi.

Generik funksiya #

KOTLIN
fun <T> ikkinchisi(royxat: List<T>): T? =
    if (royxat.size >= 2) royxat[1] else null

fun <T> juftlik(a: T, b: T): List<T> = listOf(a, b)

fun main() {
    println(ikkinchisi(listOf(10, 20, 30)))
    println(ikkinchisi(listOf("a", "b")))
    println(ikkinchisi(listOf(1.5)))
    println(juftlik("x", "y"))
}
Natija
20
b
null
[x, y]

<T> - tur parametri. U funksiya nomidan oldin yoziladi. Chaqirganda turni ko'rsatish shart emas - kompilyator argumentlardan topadi.

Nega Any emas

fun ikkinchisi(royxat: List<Any>): Any? ham ishlar edi. Lekin natijani ishlatish uchun har safar aylantirish kerak bo'lardi:

KOTLIN
val x = ikkinchisi(listOf(10, 20)) as Int

Generik esa turni saqlab qoladi: ikkinchisi(listOf(10, 20)) to'g'ridan-to'g'ri Int? qaytaradi. Aylantirish ham, xato xavfi ham yo'q.

Generik sinf #

KOTLIN
class Quti<T> {
    private val elementlar = mutableListOf<T>()

    fun sol(element: T) {
        elementlar.add(element)
    }

    fun ol(indeks: Int): T = elementlar[indeks]

    val soni: Int get() = elementlar.size

    override fun toString() = elementlar.toString()
}

fun main() {
    val sonlar = Quti<Int>()
    sonlar.sol(10)
    sonlar.sol(20)
    println("$sonlar (${sonlar.soni} ta)")
    println(sonlar.ol(0) + 5)

    val ismlar = Quti<String>()
    ismlar.sol("Malika")
    println(ismlar.ol(0).uppercase())
}
Natija
[10, 20] (2 ta)
15
MALIKA

sonlar.ol(0) + 5 ishladi - chunki kompilyator natija Int ekanini biladi.

Cheklovlar #

KOTLIN
fun <T : Comparable<T>> kattasi(a: T, b: T): T = if (a >= b) a else b

fun <T : Number> yigindi(royxat: List<T>): Double =
    royxat.sumOf { it.toDouble() }

fun main() {
    println(kattasi(15, 42))
    println(kattasi("olma", "anor"))
    println(kattasi(3.7, 2.1))

    println(yigindi(listOf(1, 2, 3)))
    println(yigindi(listOf(1.5, 2.5)))
}
Natija
42
olma
3.7
6.0
4.0

<T : Comparable<T>> - "T solishtirila oladigan bo'lishi shart". Usiz >= ni ishlatib bo'lmasdi.

Bir nechta cheklov #

KOTLIN
interface Nomlangan {
    val nom: String
}

class Talaba(override val nom: String, val ball: Int) :
    Nomlangan, Comparable<Talaba> {
    override fun compareTo(other: Talaba) = ball.compareTo(other.ball)
}

fun <T> engYaxshi(royxat: List<T>): String
        where T : Nomlangan, T : Comparable<T> =
    royxat.max().nom

fun main() {
    val talabalar = listOf(
        Talaba("Malika", 88),
        Talaba("Aziza", 95),
        Talaba("Husanboy", 92)
    )
    println(engYaxshi(talabalar))
}
Natija
Aziza

Ikkitadan ortiq shart bo'lsa, where ishlatiladi.

null bo'lmasligi #

KOTLIN
fun <T : Any> xavfsiz(qiymat: T): String = "Qiymat: $qiymat"

fun <T> ehtimol(qiymat: T): String = "Qiymat: $qiymat"

fun main() {
    println(xavfsiz("matn"))
    println(ehtimol("matn"))
    println(ehtimol(null))
}
Natija
Qiymat: matn
Qiymat: matn
Qiymat: null

Standart holatda T null bo'lishi mumkin. Buni taqiqlash uchun T : Any yoziladi.

Variantlik: out #

KOTLIN
open class Hayvon(val nom: String)
class It(nom: String) : Hayvon(nom)

fun hammasiniKorsat(hayvonlar: List<Hayvon>) {
    hayvonlar.forEach { println(it.nom) }
}

fun main() {
    val itlar: List<It> = listOf(It("Boryi"), It("Aqco"))
    hammasiniKorsat(itlar)
}
Natija
Boryi
Aqco

List<It> ni List<Hayvon> kutayotgan joyga berish ishladi.

Sababi: List ning e'loni List<out E> - ya'ni E faqat chiqishda ishlatiladi. Ro'yxatdan element olish mumkin, unga qo'shish mumkin emas, shuning uchun bunday almashtirish xavfsiz.

KOTLIN
open class Hayvon(val nom: String)
class It(nom: String) : Hayvon(nom)

class Qafas<out T>(private val ichi: T) {
    fun ol(): T = ichi
}

fun main() {
    val itQafasi: Qafas<It> = Qafas(It("Boryi"))
    val hayvonQafasi: Qafas<Hayvon> = itQafasi

    println(hayvonQafasi.ol().nom)
}
Natija
Boryi

out T - "T faqat chiqadi". Shu sababdan Qafas<It> ni Qafas<Hayvon> deb ishlatish mumkin.

MutableList da bunday ishlamaydi

MutableList<It> ni MutableList<Hayvon> deb berib bo'lmaydi - va bu to'g'ri.

Sabab: agar bunga ruxsat berilsa, kimdir MutableList<Hayvon> ga Mushuk qo'shib qo'yardi. Aslida esa u itlar ro'yxati edi - va undan It olmoqchi bo'lganda dastur qulardi.

MutableList<E> da E ham kiradi, ham chiqadi - shuning uchun hech qanday almashtirish xavfsiz emas.

Variantlik: in #

KOTLIN
open class Hayvon(val nom: String)
class It(nom: String) : Hayvon(nom)

class Boquvchi<in T> {
    fun boq(hayvon: T): String = "Boqildi"
}

fun main() {
    val hayvonBoquvchi: Boquvchi<Hayvon> = Boquvchi()
    val itBoquvchi: Boquvchi<It> = hayvonBoquvchi

    println(itBoquvchi.boq(It("Boryi")))
}
Natija
Boqildi

in T - "T faqat kiradi". Bu teskari yo'nalish: har qanday hayvonni boqa oladigan boquvchi, albatta, itni ham boqa oladi.

YozuvMa'nosiMisol
out TFaqat chiqadiList<out E>
in TFaqat kiradiComparable<in T>
TIkkalasiMutableList<E>

reified #

KOTLIN
inline fun <reified T> faqatShular(royxat: List<Any>): List<T> =
    royxat.filterIsInstance<T>()

inline fun <reified T> turiMi(qiymat: Any): Boolean = qiymat is T

fun main() {
    val aralash = listOf(1, "matn", 2.5, "yana", 42, true)

    println(faqatShular<Int>(aralash))
    println(faqatShular<String>(aralash))

    println(turiMi<String>("salom"))
    println(turiMi<Int>("salom"))
}
Natija
[1, 42]
[matn, yana]
true
false
Nega reified kerak

JVM da generik turlar ishlash paytida o'chib ketadi (type erasure). Ya'ni oddiy generik funksiya ichida qiymat is T deb yozib bo'lmaydi - T nima ekani noma'lum.

inline + reified birgalikda buni hal qiladi: kompilyator funksiya tanasini chaqirilgan joyga ko'chiradi va T o'rniga aniq turni qo'yadi.

Shuning uchun reified faqat inline funksiyalarda ishlaydi.

Yulduzcha proyeksiyasi #

KOTLIN
fun olchami(royxat: List<*>): String =
    "Ro'yxatda ${royxat.size} element bor"

fun birinchisi(royxat: List<*>): Any? = royxat.firstOrNull()

fun main() {
    println(olchami(listOf(1, 2, 3)))
    println(olchami(listOf("a", "b")))
    println(birinchisi(listOf(10, 20)))
    println(birinchisi(emptyList<String>()))
}
Natija
Ro'yxatda 3 element bor
Ro'yxatda 2 element bor
10
null

List<*> - "qandaydir turdagi ro'yxat". Tur nimaligi muhim bo'lmaganda ishlatiladi. Elementlar Any? sifatida ko'rinadi.

Amaliy misol #

KOTLIN
class Natija<T> private constructor(
    private val qiymat: T?,
    private val xato: String?
) {
    companion object {
        fun <T> muvaffaqiyat(qiymat: T) = Natija(qiymat, null)
        fun <T> xato(xabar: String) = Natija<T>(null, xabar)
    }

    fun <R> ozgartir(amal: (T) -> R): Natija<R> =
        if (qiymat != null) muvaffaqiyat(amal(qiymat))
        else Natija(null, xato)

    override fun toString() =
        if (qiymat != null) "OK($qiymat)" else "XATO($xato)"
}

fun main() {
    val a = Natija.muvaffaqiyat(42)
    val b = Natija.xato<Int>("topilmadi")

    println(a)
    println(b)
    println(a.ozgartir { it * 2 })
    println(b.ozgartir { it * 2 })
    println(a.ozgartir { "Qiymat: $it" })
}
Natija
OK(42)
XATO(topilmadi)
OK(84)
XATO(topilmadi)
OK(Qiymat: 42)

ozgartir funksiyasi T dan R ga o'tkazadi - ikkita turli generik tur bitta metodda.

Amaliy topshiriq
  1. Ro'yxatning oxirgi elementini qaytaruvchi generik funksiya yozing.
  2. Quti<T> sinfini Double bilan sinang.
  3. <T : Comparable<T>> bilan eng kattasini toping.
  4. Cheklovni olib tashlab, xato matnini o'qing.
  5. where bilan ikkita cheklov yozing.
  6. T : Any bilan null ni taqiqlang.
  7. List<It> ni List<Hayvon> ga bering va ishlashini ko'ring.
  8. MutableList bilan xuddi shuni qilib, xatoni o'qing.
  9. reified bilan turni tekshiruvchi funksiya yozing.
  10. List<*> qabul qiladigan funksiya yozing.

Xulosa #

  • <T> funksiya nomidan oldin yoziladi; tur odatda o'zi topiladi.
  • Generik Any dan yaxshiroq: turni saqlaydi, aylantirish kerak emas.
  • <T : Comparable<T>> - cheklov; ko'p shart uchun where.
  • Standart holatda T null bo'la oladi; T : Any buni taqiqlaydi.
  • out T - faqat chiqadi (List), in T - faqat kiradi.
  • MutableList<It> ni MutableList<Hayvon> deb berib bo'lmaydi - bu ataylab.
  • reified turni ishlash paytida ko'rsatadi, faqat inline bilan.
  • List<*> - tur muhim bo'lmaganda.

Keyingi bo'limda istisnolarni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.