17-bo‘lim

Istisnolar va Result

try/catch/finally, try ning ifoda ekani, o'z istisno sinfingiz, require va check, Nothing turi hamda runCatching bilan Result.

🕑 12 daqiqa o‘qish 📄 623 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. try va catch
  2. try - bu ifoda
  3. Bir necha catch va finally
  4. Kotlin da tekshiriladigan istisnolar yo'q
  5. Istisno ko'tarish
  6. require, check va error
  7. O'z istisno sinfingiz
  8. Nothing turi
  9. runCatching va Result
  10. Xatolarni boshqarishni tanlash
  11. Xulosa

Istisno - dastur ishlayotganda yuz beradigan kutilmagan holat. Kotlin da ular Java dagidek ishlaydi, lekin bir muhim farq bilan.

try va catch #

KOTLIN
fun main() {
    try {
        val son = "salom".toInt()
        println(son)
    } catch (e: NumberFormatException) {
        println("Bu son emas")
    }

    println("Dastur davom etmoqda")
}
Natija
Bu son emas
Dastur davom etmoqda

try - bu ifoda #

KOTLIN
fun oqi(matn: String): Int = try {
    matn.toInt()
} catch (e: NumberFormatException) {
    -1
}

fun main() {
    println(oqi("456"))
    println(oqi("salom"))

    val natija = try {
        10 / 0
    } catch (e: ArithmeticException) {
        0
    }
    println(natija)
}
Natija
456
-1
0

if va when kabi, try ham qiymat qaytaradi. Blokning oxirgi ifodasi natija bo'ladi.

Ammo bu yerda toIntOrNull yaxshiroq

Yuqoridagi oqi funksiyasi ishlaydi, lekin Kotlin da uni shunday yozish kerak:

KOTLIN
fun oqi(matn: String): Int = matn.toIntOrNull() ?: -1

Qisqaroq, tezroq (istisno ko'tarish qimmat amal) va o'qishliroq.

Umumiy qoida: kutilgan holat uchun istisno ishlatmang. Foydalanuvchi noto'g'ri yozishi - bu kutilgan holat.

Bir necha catch va finally #

KOTLIN
fun sinov(royxat: List<Int>, indeks: Int, bolovchi: Int) {
    try {
        println(royxat[indeks] / bolovchi)
    } catch (e: ArithmeticException) {
        println("Nolga bo'lib bo'lmaydi")
    } catch (e: IndexOutOfBoundsException) {
        println("Bunday indeks yo'q")
    } catch (e: Exception) {
        println("Boshqa xato: ${e::class.simpleName}")
    } finally {
        println("  finally bajarildi")
    }
}

fun main() {
    val sonlar = listOf(10, 20, 30)
    sinov(sonlar, 0, 2)
    sinov(sonlar, 1, 0)
    sinov(sonlar, 99, 2)
}
Natija
5
  finally bajarildi
Nolga bo'lib bo'lmaydi
  finally bajarildi
Bunday indeks yo'q
  finally bajarildi

catch bloklari yuqoridan pastga tekshiriladi, shuning uchun Exception eng oxirida bo'lishi kerak.

finally esa har doim bajariladi.

Kotlin da tekshiriladigan istisnolar yo'q #

Java dan eng katta farq

Java da ba'zi istisnolarni majburan ushlash kerak: metod throws IOException deb e'lon qilgan bo'lsa, chaqiruvchi uni try/catch ga olishi yoki o'zi ham throws yozishi shart.

Kotlin da bunday majburiyat yo'q. Har qanday istisnoni ushlash ixtiyoriy.

Sababi tajribadan kelib chiqqan: Java dasturchilari majburiyatdan qutulish uchun bo'sh catch bloklari yozib tashlashardi - bu esa xatolarni jimgina yutib yuborardi va holatni yomonlashtirardi.

Buning evaziga mas'uliyat sizga o'tadi: qaysi funksiya istisno ko'tarishi mumkinligini hujjatdan yoki koddan bilishingiz kerak.

Istisno ko'tarish #

KOTLIN
fun yoshTekshir(yosh: Int) {
    if (yosh < 0) {
        throw IllegalArgumentException("Yosh manfiy bo'lmaydi: $yosh")
    }
    println("Qabul qilindi: $yosh")
}

fun main() {
    yoshTekshir(25)

    try {
        yoshTekshir(-5)
    } catch (e: IllegalArgumentException) {
        println("Ushlandi: ${e.message}")
    }
}
Natija
Qabul qilindi: 25
Ushlandi: Yosh manfiy bo'lmaydi: -5

require, check va error #

KOTLIN
class Hisob(val egasi: String, boshlangich: Int) {
    var qoldiq = boshlangich
        private set

    init {
        require(egasi.isNotBlank()) { "Egasi ko'rsatilmagan" }
        require(boshlangich >= 0) { "Boshlang'ich summa manfiy" }
    }

    fun yech(summa: Int) {
        require(summa > 0) { "Summa musbat bo'lishi kerak" }
        check(summa <= qoldiq) { "Qoldiq yetarli emas" }
        qoldiq -= summa
    }
}

fun sinab(nom: String, ish: () -> Unit) {
    try {
        ish()
        println("$nom: muvaffaqiyat")
    } catch (e: Exception) {
        println("$nom: ${e::class.simpleName} - ${e.message}")
    }
}

fun main() {
    val h = Hisob("Husanboy", 500000)

    sinab("to'g'ri") { h.yech(100000) }
    sinab("manfiy") { h.yech(-5) }
    sinab("ko'p") { h.yech(9000000) }
    sinab("bo'sh ism") { Hisob("", 100) }

    println("Qoldiq: ${h.qoldiq}")
}
Natija
to'g'ri: muvaffaqiyat
manfiy: IllegalArgumentException - Summa musbat bo'lishi kerak
ko'p: IllegalStateException - Qoldiq yetarli emas
bo'sh ism: IllegalArgumentException - Egasi ko'rsatilmagan
Qoldiq: 400000
FunksiyaQachonQaysi istisno
requireArgument noto'g'riIllegalArgumentException
checkObyekt holati noto'g'riIllegalStateException
error(...)Bo'lmasligi kerak bo'lgan holatIllegalStateException

Farqni eslab qolish oson: require - "chaqiruvchi xato qildi", check - "hozirgi holatda bu amalni bajarib bo'lmaydi".

O'z istisno sinfingiz #

KOTLIN
class QoldiqYetmadiException(val yetishmayotgan: Int) :
    Exception("Hisobda $yetishmayotgan so'm yetishmayapti")

class Hisob(private var qoldiq: Int) {
    fun yech(summa: Int) {
        if (summa > qoldiq) throw QoldiqYetmadiException(summa - qoldiq)
        qoldiq -= summa
        println("Yechildi. Qoldiq: $qoldiq")
    }
}

fun main() {
    val h = Hisob(500000)

    try {
        h.yech(200000)
        h.yech(900000)
    } catch (e: QoldiqYetmadiException) {
        println(e.message)
        println("Yetishmayotgan: ${e.yetishmayotgan}")
    }
}
Natija
Yechildi. Qoldiq: 300000
Hisobda 600000 so'm yetishmayapti
Yetishmayotgan: 600000

O'z sinfingizning asosiy foydasi - qo'shimcha ma'lumot tashiy olishi. Xabar matnidan son ajratib olishga urinmang; kerakli qiymatni xossa qilib qo'shing.

Nothing turi #

KOTLIN
fun xatoBilanTugat(xabar: String): Nothing =
    throw IllegalStateException(xabar)

fun oqi(matn: String?): String {
    val toza = matn?.trim() ?: xatoBilanTugat("qiymat berilmadi")
    return toza.uppercase()
}

fun main() {
    println(oqi("  salom  "))

    try {
        oqi(null)
    } catch (e: IllegalStateException) {
        println("Ushlandi: ${e.message}")
    }
}
Natija
SALOM
Ushlandi: qiymat berilmadi

Nothing - "bu funksiya hech qachon qaytmaydi" degani. Kompilyator buni biladi, shuning uchun ?: dan keyin toza ni String (String? emas) deb hisoblaydi.

runCatching va Result #

KOTLIN
fun main() {
    val a = runCatching { "456".toInt() }
    val b = runCatching { "salom".toInt() }

    println(a.isSuccess)
    println(b.isFailure)
    println(a.getOrNull())
    println(b.getOrNull())
    println(b.getOrDefault(-1))
    println(a.map { it * 2 }.getOrNull())

    val natija = b.fold(
        onSuccess = { "OK: $it" },
        onFailure = { "Xato: ${it::class.simpleName}" }
    )
    println(natija)
}
Natija
true
true
456
null
-1
912
Xato: NumberFormatException

runCatching istisnoni qiymatga aylantiradi. Natija - Result<T> obyekti, uni try/catch siz tekshirish mumkin.

runCatching hamma narsani ushlaydi

runCatching har qanday Throwable ni ushlaydi - OutOfMemoryError ni ham.

Bunday xatolarni ushlab, davom etishga urinish odatda noto'g'ri: dastur allaqachon ishonchsiz holatda.

Shuning uchun runCatching ni kutilgan, aniq xatolar uchun ishlating - fayl o'qish, tarmoq so'rovi, tahlil qilish kabi. Butun dasturni unga o'rab qo'ymang.

Xatolarni boshqarishni tanlash #

HolatTo'g'ri yo'l
Foydalanuvchi noto'g'ri son yozditoIntOrNull()
Qiymat bo'lmasligi mumkinnull qaytaring
Argument noto'g'rirequire
Obyekt holati noto'g'richeck
Fayl yoki tarmoqtry/catch yoki runCatching
Dasturchining xatosiUshlamang - kod tuzatilsin
Bo'sh catch yozmang
KOTLIN
try {
    muhimAmal()
} catch (e: Exception) {
}

Bu eng yomon namuna: xato yuz berdi, lekin hech kim bilmaydi. Dastur "ishlayotgandek" ko'rinadi, aslida esa ish bajarilmagan.

Hech bo'lmaganda xabarni jurnalga yozing. Agar xatoni haqiqatan e'tiborsiz qoldirish kerak bo'lsa, nima uchun ekanini izohda yozib qo'ying.

Amaliy topshiriq
  1. Nolga bo'lishni try/catch bilan ushlang.
  2. try ni ifoda sifatida ishlatib, natijani o'zgaruvchiga oling.
  3. O'sha ishni toIntOrNull() bilan qayta yozing.
  4. finally har doim bajarilishini isbotlang.
  5. require va check farqini bitta sinfda ko'rsating.
  6. O'z istisno sinfingizni qo'shimcha xossa bilan yozing.
  7. Nothing qaytaradigan funksiya yozing.
  8. runCatching bilan xatoni qiymatga aylantiring.
  9. Result.fold bilan ikkala holatni ishlang.
  10. Qaysi holatda istisno, qaysida null to'g'ri kelishini yozing.

Xulosa #

  • try Kotlin da ifoda - u qiymat qaytaradi.
  • catch bloklari aniqrog'idan umumiyga; finally har doim bajariladi.
  • Kotlin da tekshiriladigan istisnolar yo'q - mas'uliyat sizda.
  • require - argument uchun, check - holat uchun.
  • O'z istisno sinfingiz qo'shimcha ma'lumot tashiy oladi.
  • Nothing - hech qachon qaytmaydigan funksiya turi.
  • runCatching istisnoni Result qiymatiga aylantiradi, lekin hamma narsani ushlaydi.
  • Kutilgan holat uchun istisno emas, null yoki OrNull funksiyalarini ishlating.

Keyingi bo'limda fayllar bilan ishlaymiz.

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.