Ders 23 / 23
İstisna Yönetimi
Denetlenmeyen istisna modelinin, try ifadesinin, Result'ın ve eş yordamdaki yayılmanın ölçümü; aynı mesajlı iki istisnanın eşit sayılmadığının gösterilmesi ve kursun kapanışı.
Yayın Güncelleme
Bu derste
- Kotlin'de istisna bildirmenin gerekmediğini ve try'ın değer üreten bir ifade olduğunu ölçer
- runCatching, Result, getOrElse ve exceptionOrNull ile hatayı değer olarak taşır
- coroutineScope içinde bir kardeşin başarısızlığının diğerini iptal edip dışarıya yayıldığını gösterir
- Aynı mesajla kurulan iki istisnanın eşit sayılmadığını ve veri sınıfı istisnanın içerikle karşılaştırıldığını ölçer
İçindekiler
Önceki ders iki akışın eşit sayılmadığını ölçtü. Bu son ders değer yerine bir hatanın eşit olup olmadığını sorar: aynı mesajı taşıyan iki istisna eşit midir? Önce Kotlin’in hata modeli okunur. Java’da bir fonksiyon hangi istisnaları fırlatabileceğini throws ile bildirir ve çağıran onları yakalamak zorundadır. Kotlin’de böyle bir bildirim yoktur; bütün istisnalar denetlenmeyen istisna sayılır, yakalamak çağıranın isteğine kalır. Fırlatma throw ile, yakalama try ve catch ile yapılır. Ölçü sorusu yine aynıdır: bu iki değeri eşit yapan ne?
// Criterion.kt — iki degeri esit yapan olcutu siniflandirir
class Criterion(val label: String, first: Any?, twin: Any?, other: Any?) {
val sameObject = first === twin
val equalTwin = first == twin
val equalOther = first == other
val name: String = when {
sameObject -> "tekil"
!equalTwin -> "kimlik"
!equalOther -> "icerik"
else -> "kismi-icerik"
}
fun report() {
println("$label: ayni-nesne=$sameObject, ikiz=$equalTwin, farkli=$equalOther -> $name")
}
}
fun summarize(checks: List<Criterion>) {
for (name in listOf("tekil", "kimlik", "icerik", "kismi-icerik")) {
println("$name: ${checks.count { it.name == name }}")
}
}
Denetlenmeyen İstisna ve try İfadesi
Aşağıdaki blok iki şeyi ölçer. readDisk Java’nın IOException istisnasını fırlatır, ama imzasında hiçbir bildirim yoktur ve derleyici bir şey istemez. try ise ifadedir: blok son ifadesinin değerini verir, catch dalı kendi değerini verir ve finally her durumda koşar ama değer üretmez. Günlük listesi sırayı gösterir.
// Main.kt — denetlenmeyen istisna, try ifadesi ve finally
import java.io.IOException
fun readDisk(): String {
throw IOException("disk")
}
fun withdraw(balance: Int, amount: Int): Int {
if (amount > balance) throw IllegalStateException("yetersiz")
return balance - amount
}
fun main() {
val log = mutableListOf<String>()
val left = try {
log.add("dene")
withdraw(100, 500)
} catch (e: IllegalStateException) {
log.add("yakala")
-1
} finally {
log.add("son")
}
println(left)
println(log)
try {
readDisk()
} catch (e: IOException) {
println(e::class.simpleName)
}
println(withdraw(100, 30))
}
-1 [dene, yakala, son] IOException 70
Birinci satır -1: withdraw fırlattığı için try bloğunun değeri atılıp catch dalının -1 değeri left’e atandı. Günlük [dene, yakala, son] sırasını söyler; finally en sonda, değer belli olduktan sonra koştu. Üçüncü satır IOException adını verir; catch yazıldığı için yakalandı, yazılmasaydı da derleme geçerdi. Son satır fırlatmayan yolun 70 döndüğünü gösterir.
KE35. Bu ölçümler tek iş parçacıklı, return içermeyen try ifadeleri için. finally içinden return ya da yeni istisna fırlatmanın sonucu, use ile kaynak kapatma ve Error sınıfları ölçülmedi.
runCatching ve Result
Hatayı yakalamak yerine değer olarak taşımak için runCatching { } kullanılır. Blok döndüyse Result başarılı, fırlattıysa başarısızdır. getOrElse başarısızlıkta yedek değeri verir, exceptionOrNull başarılıysa null, değilse istisnanın kendisini verir. Önce yardımcı dosyada kendi istisna sınıfı tanımlanır; ana blok onu fırlatan bir fonksiyon kurar.
// Errors.kt — kendi istisna sinifi
class LimitException(val limit: Int) : Exception("limit asildi")
// Main.kt — runCatching ve Result
fun parse(text: String): Int {
if (text.length > 3) throw LimitException(3)
return text.toInt()
}
fun main() {
val ok = runCatching { parse("42") }
val badFormat = runCatching { parse("x") }
val tooLong = runCatching { parse("12345") }
println(ok.getOrElse { -1 })
println(badFormat.getOrElse { -1 })
println(ok.exceptionOrNull())
println(badFormat.exceptionOrNull()?.let { it::class.simpleName })
val error = tooLong.exceptionOrNull()
if (error is LimitException) {
println(error.limit)
}
}
42 -1 null NumberFormatException 3
ok başarılı olduğu için getOrElse 42 verdi; "x".toInt() NumberFormatException fırlattığı için ikinci satır yedek değer olan -1. Üçüncü satır başarılı sonuçta exceptionOrNull’un null olduğunu, dördüncü satır başarısız sonuçta istisnanın tipini gösterir. Son satır kendi istisna sınıfının taşıdığı limit değerini okur: is denetimi sonrası error akıllı tip dönüşümüyle LimitException olur.
KE36. Bu ölçümler Int sonuçlu runCatching çağrıları ve Exception altındaki bir sınıf için. map, recover ve onFailure zincirleri, getOrThrow ve Result’ın fonksiyon dönüş tipi olarak kullanımı ölçülmedi.
Eş Yordamda Yayılma
Yapılandırılmış eşzamanlılıkta bir çocuk eş yordam başarısız olursa kapsam iptal edilir: kardeşler iptal alır ve istisna coroutineScope’tan dışarı çıkar. Aşağıdaki blokta ilk çocuk bin milisaniye bekleyecek, ikincisi hemen fırlatacak. Dışarıda yakalanan istisnanın yalnız tipi basılır, mesajı basılmaz.
// Main.kt — eş yordamda istisnanın yayılması
import kotlinx.coroutines.*
fun main() = runBlocking {
val log = mutableListOf<String>()
val caught = try {
coroutineScope {
launch {
try {
delay(1000)
log.add("bitti")
} finally {
log.add("iptal")
}
}
launch {
throw IllegalStateException("kardes")
}
}
"yok"
} catch (e: Exception) {
e::class.simpleName
}
println(caught)
println(log)
}
IllegalStateException [iptal]
Dışarıda yakalanan istisna çocuğun fırlattığı IllegalStateException’dır. Günlükte yalnız iptal vardır: bekleyen çocuğa iptal geldi, finally bloğu koştu ve bitti hiç yazılmadı. coroutineScope çağrısından sonraki "yok" satırına ise hiç gelinmedi. İstisna, eş yordamın kapsamını aşıp sıradan bir try ile yakalanabilir.
KE37. Bu ölçümler tek coroutineScope, tek runBlocking ve bir başarısız çocuk için. supervisorScope, CoroutineExceptionHandler ve birden çok başarısız çocuğun istisnalarının nasıl toplandığı ölçülmedi.
Ölçüt: Kimlik
Şimdi asıl soru. Aynı mesajla kurulan iki istisna eşit midir? Throwable sınıfı equals tanımlamaz, kimliğe düşer. Sınırlayıcı ölçümler Result’ı ve veri sınıfı olarak yazılan bir istisnayı ekler. Result.failure içindeki istisnayı taşır: iki ayrı istisna iki ayrı Result yapar, aynı istisna iki Result’ı eşit yapar.
// Main.kt — istisnanın ölçütü ve sınırlayıcı ölçümler
data class CodedError(val code: Int) : Exception("kod")
fun main() {
val shared = IllegalStateException("m")
val checks = listOf(
Criterion("istisna", IllegalStateException("m"), IllegalStateException("m"), IllegalStateException("x")),
Criterion("sonuc-iki-istisna", Result.failure<Int>(IllegalStateException("m")), Result.failure<Int>(IllegalStateException("m")), Result.failure<Int>(IllegalStateException("x"))),
Criterion("sonuc-ayni-istisna", Result.failure<Int>(shared), Result.failure<Int>(shared), Result.failure<Int>(IllegalStateException("x"))),
Criterion("sonuc-basari", Result.success(1), Result.success(1), Result.success(2)),
Criterion("veri-sinifi-istisna", CodedError(7), CodedError(7), CodedError(8)),
)
for (check in checks) check.report()
summarize(checks)
}
istisna: ayni-nesne=false, ikiz=false, farkli=false -> kimlik sonuc-iki-istisna: ayni-nesne=false, ikiz=false, farkli=false -> kimlik sonuc-ayni-istisna: ayni-nesne=false, ikiz=true, farkli=false -> icerik sonuc-basari: ayni-nesne=false, ikiz=true, farkli=false -> icerik veri-sinifi-istisna: ayni-nesne=false, ikiz=true, farkli=false -> icerik tekil: 0 kimlik: 2 icerik: 3 kismi-icerik: 0
İlk satır bu dersin ölçütüdür: aynı mesajlı iki istisna eşit değildir, ölçüt kimlik. İkinci satırda iki ayrı istisnayı saran Result’lar da eşit değildir. Üçüncü satırda aynı istisnayı saran iki ayrı Result nesnesi eşittir: Result istisnanın ölçütünü devralır. Result.success(1) iki kez içerik verir. Veri sınıfı olarak yazılan CodedError ise aynı koduyla eşittir, çünkü üretilen equals, Exception’dan devralınan kimlik eşitliğini yeniden tanımlar. Sınırlayıcı ölçüm şudur: istisnanın ölçütünü tipi belirler, mesaj belirlemez.
KE38. Bu ölçümler IllegalStateException ve Result için. İstisnanın cause zinciri, stackTrace içeriği ve equals’i yazılmış başka istisna sınıfları ölçülmedi.
Özet
- Kotlin’de
throwsyoktur; bütün istisnalar denetlenmeyen istisnadır.trydeğer üreten bir ifadedir vefinallyen sonda koşar. runCatchinghatayıResultolarak taşır;getOrElseyedek değer,exceptionOrNullistisnayı verir.coroutineScopeiçinde bir çocuk başarısız olunca kardeşler iptal edilir ve istisna dışarıya yayılır.- Aynı mesajlı iki istisna eşit değildir. Bu dersin ölçütü: kimlik.
- Sınırlayıcı ölçüm:
Result.failureistisnanın ölçütünü taşır; veri sınıfı olarak yazılan istisna aynı alanla içeriktir.
Kurs Kapanışı
Kurs yirmi üç dersle tek soruyu sordu ve her dersin ölçütü kaynak dersin özetinden alındı:
| Ders | Ölçülen değer | Ölçüt | Sınırlayıcı ölçüm |
|---|---|---|---|
| Kotlin’in Konumu | veri sınıfı, JVM listesi | içerik | kurucudaki name farklıysa eşit değil |
| Değişkenler | const val sabiti |
tekil | çalışma anında kurulan aynı metin içerik |
| Temel Tipler | kutulanan 1000 |
içerik | 127 tekil; NaN statik tipe göre değişir |
| Dizgi Şablonları | iki ayrı şablon | içerik | tek değişkenli şablon tekil |
| Denetim Akışı | when dalı, sıradan sınıf |
kimlik | is Box tutar; iki 1..3 içerik |
| Fonksiyonlar | iki ::plain |
içerik | listOf() varsayılanı tekil |
| Lambdalar | iki ayrı lambda | kimlik | yakalamayan lambda tekil; ::inc içerik |
| Kapsam Fonksiyonları | let ile copy |
içerik | also ikizi tekil, ölçümü bozar |
| Null Güvenliği | null ile null |
tekil | dolu iki dizgi içerik; dolu ile null kimlik |
| Güvenli Çağrı ve Elvis | iki güvenli çağrı | içerik | boş alıcıda iki null tekil |
| Sınıflar ve Yapıcılar | equals’siz sınıf |
kimlik | aynı kalıp equals’e göre içerik, kısmi içerik |
| Veri Sınıfları | copy ile kurulan nesne |
içerik | IntArray alanı kimlik, List alanı içerik |
| Mühürlü Tipler ve Numaralandırmalar | numaralandırma girdisi | tekil | alt tipin türü ölçütü belirler |
| Nesne Bildirimleri | iki okuma | tekil | nesne ifadesi her seferinde yeni, kimlik |
| Arayüzler ve Devretme | by ile devreden iki nesne |
kimlik | aynı temsilcili iki veri sınıfı eşit |
| Uzantı Fonksiyonları | uzantı equals |
kimlik | üye uzantıyı yener |
| Genellikler ve Varyans | iki boş liste, iki tip parametresi | tekil | genel veri sınıfı alan değerine bakar |
| Koleksiyon Türleri | liste eşitliği | içerik | küme sırası kısmi içerik; boş liste tekil |
| Dönüşüm İşlemleri | iki map |
içerik | distinct öğenin ölçütünü kullanır |
| Tembel Diziler | iki sequenceOf |
kimlik | toList() ile içerik |
| Eş Yordamlar | iki Deferred |
kimlik | await sonuçları içerik |
| Akışlar | distinctUntilChanged |
içerik | sıradan sınıfta 5 değer kalır; akış kimlik |
| İstisna Yönetimi | aynı mesajlı iki istisna | kimlik | Result istisnanın ölçütünü taşır; veri sınıfı istisna içerik |
Yirmi üç dersin dağılımı: tekil 5, kimlik 8, içerik 10, kısmi içerik 0. Kısmi içerik hiçbir dersin ölçütü olmadı; yalnız sınırlayıcı ölçümlerde göründü. Açılış dersinin çekirdeği ayrı bir sayımdır: sekiz değerin dağılımı 2/2/3/1’dir (tekil/kimlik/içerik/kısmi içerik). İkisi aynı şey değildir, çünkü çekirdek sekiz değer, tablo yirmi üç ders sayar.
Aynı mesajla kurulan iki istisnanın eşit çıkmaması, kursun bütün cevabını toplar: Kotlin’de eşitliği değerin görünüşü değil tipin kuralı verir. Sıradaki kurs Android Platformu ve Araçlar’dır; uygulamanın parçalarını ve bu parçaların yaşam döngülerini konu alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.