16-bo‘lim
Generiklar
Generik funksiya va sinflar, cheklovlar, in va out variantligi, reified turlar hamda yulduzcha proyeksiyasi.
Ushbu bo‘lim mundarijasi
Generik - turni keyinroq aytishga imkon beradigan yozuv.
List<String> va List<Int> - bitta sinfning ikki ko'rinishi.
Generik funksiya #
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"))
}
20
b
null
[x, y]
<T> - tur parametri. U funksiya nomidan oldin yoziladi.
Chaqirganda turni ko'rsatish shart emas - kompilyator argumentlardan
topadi.
fun ikkinchisi(royxat: List<Any>): Any? ham ishlar edi. Lekin
natijani ishlatish uchun har safar aylantirish kerak bo'lardi:
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 #
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())
}
[10, 20] (2 ta)
15
MALIKA
sonlar.ol(0) + 5 ishladi - chunki kompilyator natija Int ekanini
biladi.
Cheklovlar #
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)))
}
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 #
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))
}
Aziza
Ikkitadan ortiq shart bo'lsa, where ishlatiladi.
null bo'lmasligi #
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))
}
Qiymat: matn
Qiymat: matn
Qiymat: null
Standart holatda T null bo'lishi mumkin. Buni taqiqlash uchun
T : Any yoziladi.
Variantlik: out #
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)
}
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.
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)
}
Boryi
out T - "T faqat chiqadi". Shu sababdan Qafas<It> ni
Qafas<Hayvon> deb ishlatish mumkin.
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 #
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")))
}
Boqildi
in T - "T faqat kiradi". Bu teskari yo'nalish: har qanday hayvonni
boqa oladigan boquvchi, albatta, itni ham boqa oladi.
| Yozuv | Ma'nosi | Misol |
|---|---|---|
out T | Faqat chiqadi | List<out E> |
in T | Faqat kiradi | Comparable<in T> |
T | Ikkalasi | MutableList<E> |
reified #
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"))
}
[1, 42]
[matn, yana]
true
false
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 #
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>()))
}
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 #
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" })
}
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.
- Ro'yxatning oxirgi elementini qaytaruvchi generik funksiya yozing.
Quti<T>sinfiniDoublebilan sinang.<T : Comparable<T>>bilan eng kattasini toping.- Cheklovni olib tashlab, xato matnini o'qing.
wherebilan ikkita cheklov yozing.T : Anybilannullni taqiqlang.List<It>niList<Hayvon>ga bering va ishlashini ko'ring.MutableListbilan xuddi shuni qilib, xatoni o'qing.reifiedbilan turni tekshiruvchi funksiya yozing.List<*>qabul qiladigan funksiya yozing.
Xulosa #
<T>funksiya nomidan oldin yoziladi; tur odatda o'zi topiladi.- Generik
Anydan yaxshiroq: turni saqlaydi, aylantirish kerak emas. <T : Comparable<T>>- cheklov; ko'p shart uchunwhere.- Standart holatda
Tnullbo'la oladi;T : Anybuni taqiqlaydi. out T- faqat chiqadi (List),in T- faqat kiradi.MutableList<It>niMutableList<Hayvon>deb berib bo'lmaydi - bu ataylab.reifiedturni ishlash paytida ko'rsatadi, faqatinlinebilan.List<*>- tur muhim bo'lmaganda.
Keyingi bo'limda istisnolarni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.