13-bo‘lim
Vorislik, interfeys va sealed
open va override, abstract sinflar, interfeys va undagi tayyor kod, sealed sinflar hamda ular bilan when ning kuchi.
Ushbu bo‘lim mundarijasi
Vorislik - mavjud sinf ustiga qurish. Kotlin da u Java dagidan qat'iyroq: har bir kengaytirish ataylab ruxsat etilishi kerak.
open - kengaytirishga ruxsat #
open class Hayvon(val nom: String) {
open fun ovoz() = "..."
fun tavsif() = "$nom: ${ovoz()}"
}
class It(nom: String) : Hayvon(nom) {
override fun ovoz() = "Vov-vov!"
}
class Mushuk(nom: String) : Hayvon(nom) {
override fun ovoz() = "Miyov!"
}
fun main() {
val hayvonlar = listOf(It("Boryi"), Mushuk("Mosh"), Hayvon("Noma'lum"))
for (h in hayvonlar) {
println(h.tavsif())
}
}
Boryi: Vov-vov!
Mosh: Miyov!
Noma'lum: ...
Uchta qoida:
- Sinf
openbo'lmasa, undan meros olib bo'lmaydi. - Metod
openbo'lmasa, uni qayta yozib bo'lmaydi. - Qayta yozishda
overridemajburiy.
Java da har qanday sinfdan meros olish mumkin - buni taqiqlash uchun
final yozish kerak.
Kotlin da esa aksincha: standart holat yopiq, ochish uchun open
yoziladi.
Sababi amaliy. Sinf muallifi uning qaysi qismi kengaytirish uchun mo'ljallanganini biladi. Tasodifan meros olingan sinf esa keyinchalik o'zgartirilganda vorislarni buzadi - bu "mo'rt asos sinf" muammosi deb ataladi.
open yozish - "men bu yerni kengaytirish mumkinligini o'ylab
ko'rdim" degan ochiq va'da.
super #
open class Xodim(val ism: String, val maosh: Int) {
open fun tavsif() = "$ism, maosh: $maosh"
}
class Boshliq(ism: String, maosh: Int, val bolim: String) :
Xodim(ism, maosh) {
override fun tavsif() = super.tavsif() + ", $bolim bo'limi boshlig'i"
}
fun main() {
println(Boshliq("Karimov", 12000000, "Savdo").tavsif())
}
Karimov, maosh: 12000000, Savdo bo'limi boshlig'i
super.tavsif() - ota sinfning versiyasini ishlatib, ustiga qo'shish.
abstract #
abstract class Shakl(val nom: String) {
abstract fun yuza(): Double
fun korsat() = "$nom yuzasi: ${"%.2f".format(yuza())}"
}
class Aylana(val radius: Double) : Shakl("Aylana") {
override fun yuza() = Math.PI * radius * radius
}
class Torburchak(val a: Double, val b: Double) : Shakl("To'rtburchak") {
override fun yuza() = a * b
}
fun main() {
val shakllar = listOf(Aylana(3.0), Torburchak(4.0, 5.0))
shakllar.forEach { println(it.korsat()) }
}
Aylana yuzasi: 28.27
To'rtburchak yuzasi: 20.00
abstract class- undan obyekt yasab bo'lmaydi.abstract fun- tanasi yo'q, voris yozishi shart.
abstract a'zolar avtomatik open - alohida yozish kerak emas.
Interfeys #
interface Saqlanadigan {
val nomi: String
fun saqla(): String
fun tavsif() = "Saqlanadigan: $nomi"
}
class Hujjat(override val nomi: String) : Saqlanadigan {
override fun saqla() = "$nomi diskka yozildi"
}
class Rasm : Saqlanadigan {
override val nomi = "rasm.png"
override fun saqla() = "Rasm siqilib saqlandi"
override fun tavsif() = "Tasvir fayli: $nomi"
}
fun main() {
val narsalar = listOf(Hujjat("hisobot.docx"), Rasm())
for (n in narsalar) {
println(n.tavsif())
println(" ${n.saqla()}")
}
}
Saqlanadigan: hisobot.docx
hisobot.docx diskka yozildi
Tasvir fayli: rasm.png
Rasm siqilib saqlandi
Kotlin interfeysida:
- Xossa bo'lishi mumkin (
val nomi). - Tayyor kod bo'lishi mumkin (
fun tavsif()), va vorislar uni qayta yozishi shart emas. - Lekin holat (saqlanadigan qiymat) bo'lolmaydi.
Bir necha interfeys #
interface Chiziladigan {
fun chiz(): String
}
interface Olchanadigan {
fun yuza(): Double
}
class Kvadrat(val tomon: Double) : Chiziladigan, Olchanadigan {
override fun chiz() = "Tomoni $tomon bo'lgan kvadrat"
override fun yuza() = tomon * tomon
}
fun main() {
val k = Kvadrat(4.0)
println(k.chiz())
println(k.yuza())
val narsalar: List<Any> = listOf(k, "matn", 42)
for (n in narsalar) {
if (n is Olchanadigan) println("Yuzasi: ${n.yuza()}")
}
}
Tomoni 4.0 bo'lgan kvadrat
16.0
Yuzasi: 16.0
Sinf bitta sinfdan meros oladi, lekin ko'p interfeysni bajaradi.
abstract class | interface | |
|---|---|---|
| Holat (maydon) saqlaydimi | Ha | Yo'q |
| Konstruktor bormi | Ha | Yo'q |
| Nechtasini bajarish mumkin | Bitta | Ko'p |
| Qachon | Umumiy holat va kod bo'lsa | Faqat qobiliyat bo'lsa |
sealed - yopiq ierarxiya #
sealed class Natija
data class Muvaffaqiyat(val qiymat: Int) : Natija()
data class Xato(val xabar: String) : Natija()
object Kutilmoqda : Natija()
fun korsat(n: Natija): String = when (n) {
is Muvaffaqiyat -> "Natija: ${n.qiymat}"
is Xato -> "Xato: ${n.xabar}"
Kutilmoqda -> "Kutilmoqda..."
}
fun main() {
val holatlar = listOf(
Muvaffaqiyat(42),
Xato("tarmoq uzildi"),
Kutilmoqda
)
holatlar.forEach { println(korsat(it)) }
}
Natija: 42
Xato: tarmoq uzildi
Kutilmoqda...
sealed - "bu sinfning vorislari faqat shu yerda e'lon
qilinadi" degani.
Yuqoridagi when da else yo'q, va kompilyator shikoyat
qilmadi: u Natija ning hamma vorisini biladi va hammasi qamrab
olinganini ko'rdi.
Endi yangi voris qo'shsangiz - masalan object BekorQilindi -
kompilyator shu when ni ko'rsatib xato beradi.
Bu juda qimmatli: yangi holat qo'shganda uni qayerda hisobga olish
kerakligini kompilyator o'zi aytadi. else yozgan bo'lsangiz,
yangi holat jimgina "else" ga tushib ketardi va xato faqat
ishlaganda bilinardi.
sealed bilan amaliy misol #
sealed interface Tolov {
val summa: Int
}
data class Naqd(override val summa: Int) : Tolov
data class Karta(override val summa: Int, val raqam: String) : Tolov
data class Kochirma(override val summa: Int, val hisob: String) : Tolov
fun komissiya(t: Tolov): Int = when (t) {
is Naqd -> 0
is Karta -> t.summa / 100
is Kochirma -> 5000
}
fun main() {
val tolovlar = listOf(
Naqd(100000),
Karta(200000, "8600****1234"),
Kochirma(500000, "2020...")
)
for (t in tolovlar) {
val nom = t::class.simpleName
println("$nom: ${t.summa}, komissiya ${komissiya(t)}")
}
println("Jami komissiya: ${tolovlar.sumOf { komissiya(it) }}")
}
Naqd: 100000, komissiya 0
Karta: 200000, komissiya 2000
Kochirma: 500000, komissiya 5000
Jami komissiya: 7000
sealed interface ham bor va ko'pincha sealed class dan
qulayroq - chunki voris boshqa sinfdan ham meros olishi mumkin.
Vorislikni tanlash #
Yangi boshlovchilar ko'pincha hamma narsani vorislik bilan hal qilishga urinadi va uch-to'rt qavatli ierarxiya quradi.
Amalda buni tushunish ham, o'zgartirish ham qiyin: ota sinfdagi kichik o'zgarish butun daraxtni buzadi.
Ko'p hollarda kompozitsiya - obyektni xossa sifatida saqlash - oddiyroq va moslashuvchanroq bo'ladi.
Transportota sinfini va ikkita vorisini yozing.opensiz sinfdan meros olishga urinib, xatoni o'qing.superbilan ota metodini chaqirib, natijaga qo'shing.abstract class Shaklni uchburchak bilan to'ldiring.- Interfeysga tayyor kodli metod qo'shing.
- Bitta sinfda ikkita interfeysni bajaring.
isbilan turni tekshirib, faqat mos obyektlarni ajrating.sealed class Natijayozib,whennielsesiz ishlating.- Unga yangi voris qo'shing va kompilyator nima deyishini ko'ring.
abstract classvainterfacefarqini uch band bilan yozing.
Xulosa #
- Kotlin da sinf va metod standart holatda yopiq; kengaytirish
uchun
openyoziladi. - Qayta yozishda
overridemajburiy;super.metod()ota versiyani chaqiradi. abstractsinfdan obyekt yasab bo'lmaydi; uning a'zolari avtomatikopen.- Interfeys xossa va tayyor kodga ega bo'la oladi, lekin holat saqlamaydi.
- Sinf bitta sinfdan meros oladi, ko'p interfeysni bajaradi.
sealed- vorislar ro'yxati yopiq; shuning uchunwhendaelsekerak emas va yangi voris qo'shilsa kompilyator ogohlantiradi.- Vorislikdan oldin kompozitsiya ni o'ylab ko'ring.
Keyingi bo'limda kengaytma funksiyalarini ko'ramiz.
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.