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.

🕑 10 daqiqa o‘qish 📄 595 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. open - kengaytirishga ruxsat
  2. super
  3. abstract
  4. Interfeys
  5. Bir necha interfeys
  6. sealed - yopiq ierarxiya
  7. sealed bilan amaliy misol
  8. Vorislikni tanlash
  9. Xulosa

Vorislik - mavjud sinf ustiga qurish. Kotlin da u Java dagidan qat'iyroq: har bir kengaytirish ataylab ruxsat etilishi kerak.

open - kengaytirishga ruxsat #

KOTLIN
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())
    }
}
Natija
Boryi: Vov-vov!
Mosh: Miyov!
Noma'lum: ...

Uchta qoida:

  1. Sinf open bo'lmasa, undan meros olib bo'lmaydi.
  2. Metod open bo'lmasa, uni qayta yozib bo'lmaydi.
  3. Qayta yozishda override majburiy.
Nega hamma narsa yopiq

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 #

KOTLIN
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())
}
Natija
Karimov, maosh: 12000000, Savdo bo'limi boshlig'i

super.tavsif() - ota sinfning versiyasini ishlatib, ustiga qo'shish.

abstract #

KOTLIN
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()) }
}
Natija
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 #

KOTLIN
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()}")
    }
}
Natija
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 #

KOTLIN
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()}")
    }
}
Natija
Tomoni 4.0 bo'lgan kvadrat
16.0
Yuzasi: 16.0

Sinf bitta sinfdan meros oladi, lekin ko'p interfeysni bajaradi.

abstract classinterface
Holat (maydon) saqlaydimiHaYo'q
Konstruktor bormiHaYo'q
Nechtasini bajarish mumkinBittaKo'p
QachonUmumiy holat va kod bo'lsaFaqat qobiliyat bo'lsa

sealed - yopiq ierarxiya #

KOTLIN
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
Natija: 42
Xato: tarmoq uzildi
Kutilmoqda...

sealed - "bu sinfning vorislari faqat shu yerda e'lon qilinadi" degani.

sealed ning asosiy foydasi - when da else kerak emas

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 #

KOTLIN
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) }}")
}
Natija
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 #

Qaysi vositani tanlash kerak interface "nima qila oladi" holat yo'q abstract class umumiy holat va kod bitta ota sealed variantlar soni aniq when to'liq bo'ladi data class faqat ma'lumot vorislik kerak emas Vorislikdan oldin o'ylab ko'ring: balki kompozitsiya to'g'riroqdir. "A - bu B" bo'lsa vorislik, "A ichida B bor" bo'lsa - oddiy xossa. Kotlin da chuqur ierarxiya kam uchraydi - bu yaxshi belgi
Aksariyat holatlarda interface yoki sealed yetarli
Vorislikni ortiqcha ishlatmang

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.

Amaliy topshiriq
  1. Transport ota sinfini va ikkita vorisini yozing.
  2. open siz sinfdan meros olishga urinib, xatoni o'qing.
  3. super bilan ota metodini chaqirib, natijaga qo'shing.
  4. abstract class Shakl ni uchburchak bilan to'ldiring.
  5. Interfeysga tayyor kodli metod qo'shing.
  6. Bitta sinfda ikkita interfeysni bajaring.
  7. is bilan turni tekshirib, faqat mos obyektlarni ajrating.
  8. sealed class Natija yozib, when ni else siz ishlating.
  9. Unga yangi voris qo'shing va kompilyator nima deyishini ko'ring.
  10. abstract class va interface farqini uch band bilan yozing.

Xulosa #

  • Kotlin da sinf va metod standart holatda yopiq; kengaytirish uchun open yoziladi.
  • Qayta yozishda override majburiy; super.metod() ota versiyani chaqiradi.
  • abstract sinfdan obyekt yasab bo'lmaydi; uning a'zolari avtomatik open.
  • 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 uchun when da else kerak emas va yangi voris qo'shilsa kompilyator ogohlantiradi.
  • Vorislikdan oldin kompozitsiya ni o'ylab ko'ring.

Keyingi bo'limda kengaytma funksiyalarini ko'ramiz.

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.