---
title: 'Denetim Akışı'
source: 'https://academia.sh/tr/kurslar/kotlin/denetim-akisi'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:39+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - '`if` ve `when` yapılarının değer üreten ifade olarak kullanılışını göstermek'
    - '`is` ile tip denetimini, `..`, `until`, `downTo` ve `step` ile aralıkları yazmak'
    - '`when` dalının değeri `==` ile eşleştirdiğini ve bunun sıradan sınıfta ne sonuç verdiğini açıklamak'
---

# Denetim Akışı

`if` ve `when` ifadelerini, `is` denetimini ve `for` döngüsünü tanıtır; `when` dalının `==` ile eşleştiğini ve sıradan sınıf örneğinde tutmadığını ölçer.

Dizgi şablonlarında aynı metnin iki biçimde kurulabildiğini gördük. Bu derste akışa geçiyoruz: bir koşula göre değer seçmek, bir değeri dallara dağıtmak, bir aralığı dolaşmak. Sonunda kimlik ölçütünü yeniden buluyoruz: `when` bir değeri bir dalla eşleştirirken `==` kullanır ve eşitliği olmayan bir örnek dalı tutturamaz.

## if, when ve for

Kotlin'de `if` bir ifadedir (expression): bir değer üretir ve o değer bir değişkene atanabilir. Ayrı bir üçlü işleç gerekmez; `val label = if (score >= 50) "gecti" else "kaldi"` yazılır. `when` de bir ifadedir. Koşulsuz biçimde her dal bir mantıksal ifade taşır ve ilk doğru dal çalışır. Bir değere bağlı biçimde ise `when (value)` yazılır; her dal ya bir değerle ya da `is` ile bir tip denetimiyle başlar. `when` ifade olarak kullanıldığında derleyici bütün durumların kapsandığından emin olmak ister: öznesiz biçimde `else` dalı gerekir; özneli biçimde ise `Boolean`, numaralandırma ya da mühürlü sınıf tüm durumlarıyla sayılmışsa `else` zorunlu değildir.

`for` döngüsü bir aralığı ya da bir koleksiyonu dolaşır. Aralık `1..3` kapalı bir aralıktır: üst sınır dahildir. `1 until 7`, üst sınırı dışarıda bırakır. `step` adımı belirler ve `3 downTo 1` geriye sayar.

Bu derste de ölçümler ilk derste tanıtılan yardımcı dosyayla yapılır. `Criterion` sınıfı, bir değer ile ikizini ve farklı bir değeri alıp üç soruyu sorar; dosya aşağıda aynen verilmiştir.

```kotlin
// 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 }}")
    }
}
```

Aşağıdaki program bunları bir arada çalıştırır. `grade` koşulsuz bir `when` ile bir puanı sözcüğe çevirir. `describe` ise `Any` bir değeri `is` ile tipine göre ayırır: `Int` ise iki katını, `String` ise uzunluğunu söyler.

```kotlin
// Main.kt — if, when, is ve for
fun grade(score: Int): String = when {
    score >= 90 -> "pekiyi"
    score >= 70 -> "iyi"
    else -> "gecer"
}

fun describe(value: Any): String = when (value) {
    is Int -> "tam sayi, iki kati ${value * 2}"
    is String -> "dizgi, uzunluk ${value.length}"
    else -> "bilinmeyen"
}

fun main() {
    val score = 82
    val label = if (score >= 50) "gecti" else "kaldi"
    println("$label, ${grade(score)}")
    println(describe(21))
    println(describe("kotlin"))
    println((1..3).joinToString(" "))
    println((1 until 7 step 2).joinToString(" "))
    println((3 downTo 1).joinToString(" "))
}
```

```
gecti, iyi
tam sayi, iki kati 42
dizgi, uzunluk 6
1 2 3
1 3 5
3 2 1
```

İlk satırda `label` değişkeni `if` ifadesiyle `gecti` değerini aldı ve `grade(82)` çağrısı `iyi` döndürdü, çünkü 82 doksanın altında, yetmişin üstündedir. İkinci ve üçüncü satırlar `describe` fonksiyonunun iki dalını gösterir: `21` bir `Int` olduğu için iki katı olan 42 basıldı, `"kotlin"` bir `String` olduğu için uzunluğu olan 6 basıldı. Bu dalların içinde `value` değişkenine tipin yeni hâliyle erişildi; bu özellik akıllı tip dönüşümü (smart cast) adını taşır. Son üç satır aralıklardır: `1..3` üçünü de verir, `1 until 7 step 2` bir, üç, beş değerlerini verir, `3 downTo 1` geriye sayar.

**DT31.** `when` ifadesi tam sayı ve dizgi dallarıyla ölçüldü; numaralandırma ve `Boolean` üzerindeki kapsayıcı `when` aşağıda ölçülür, mühürlü sınıf (sealed class) üzerindeki denetim bu dersin dışındadır.

**DT32.** Aralıklar yalnız `joinToString` ile yazdırıldı; boş bir aralığın döngüde ne yaptığı aşağıda ayrıca ölçülür.

## when Dalı == ile Eşleşir

Dersin ölçümü `when` dalının eşleşme kuralına bakar. `when (value)` içinde bir dal ifadesi yazıldığında Kotlin, `value == dal` karşılaştırmasını yapar. Bu yüzden dalın tutup tutmaması eşitlik ölçütüne bağlıdır. `branchMatches` fonksiyonu bunu doğrudan sorar: aldığı `value` değerini `branch` ile `when` üzerinden eşleştirir ve `true` ya da `false` döndürür.

Üç durum deneniyor. Birincisi `Point(1, 2)` ile `Point(1, 2)`: veri sınıfı, yani içerik ölçütü. İkincisi `Box(1)` ile `Box(1)`: sıradan sınıf, yani kimlik. Üçüncüsü aynı `box` örneğinin kendisiyle eşleşmesi. Ardından aynı `Box(1)` değerine `is Box` dalı verilir. Son olarak `Criterion` üç gözlemi sınıflandırır.

```kotlin
// Main.kt — when dali == ile eslesir
class Box(val size: Int)

data class Point(val x: Int, val y: Int)

fun branchMatches(value: Any, branch: Any): Boolean = when (value) {
    branch -> true
    else -> false
}

fun main() {
    val box = Box(1)
    println(branchMatches(Point(1, 2), Point(1, 2)))
    println(branchMatches(Box(1), Box(1)))
    println(branchMatches(box, box))
    println(when (Box(1)) {
        is Box -> "is Box dali tutar"
        else -> "tutmaz"
    })
    val checks = listOf(
        Criterion("when-dali-box", Box(1), Box(1), Box(2)),
        Criterion("when-dali-point", Point(1, 2), Point(1, 2), Point(1, 3)),
        Criterion("aralik", 1..3, 1..3, 1..4),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
true
false
true
is Box dali tutar
when-dali-box: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
when-dali-point: ayni-nesne=false, ikiz=true, farkli=false -> icerik
aralik: ayni-nesne=false, ikiz=true, farkli=false -> icerik
tekil: 0
kimlik: 1
icerik: 2
kismi-icerik: 0
```

`branchMatches(Point(1, 2), Point(1, 2))` `true` verdi: veri sınıfında `==` içeriğe bakar, dal tuttu. `branchMatches(Box(1), Box(1))` ise `false` verdi: iki `Box(1)` aynı kurucu argümanını taşısa da ayrı nesnedir ve sıradan sınıfta `==` kimliğe bakar. Aynı `box` örneği kendisiyle eşleşti, çünkü bir nesne kendisine eşittir. `is Box` dalı ise tipe bakar, değere değil: yeni kurulmuş bir `Box(1)` bile `is Box` dalını tutturur. Bu yüzden `is Box dali tutar` satırı basıldı.

Üç sınıflandırma satırı bunu özetler. `when-dali-box` kimliktir: ikiz ile `==` `false` verir. `when-dali-point` içeriktir: ikizle `==` `true`, farklı değerle `false` verir. `aralik` de içeriktir: `1..3` aralığı, ayrı kurulmuş başka bir `1..3` aralığına eşittir ve `1..4` aralığına eşit değildir. Özet satırlarında sıfır tekil, bir kimlik, iki içerik ve sıfır kısmi içerik görünür.

**DT33.** `branchMatches` fonksiyonu dalı bir değişken olarak alır; `when` içinde sabit bir dal yazılması durumunda aynı `==` kuralının geçerli olduğu bu derste ayrıca ölçülmedi.

**DT34.** Aralık eşitliği `IntRange` ile ölçüldü; `LongRange` ve `CharRange` için aynı davranış iddia edilmiyor.

## when ile Aralık, Çok Değerli Dal ve Numaralandırma

`when` dalları yalnız tek bir değerle sınırlı değildir. Aşağıdaki blok üç şeyi sorar: virgülle ayrılmış çok değerli dal, `in` ile aralık dalı ve aralığın dışını yakalayan dal birlikte kullanılınca hangi sıra ile değerlendirilir; numaralandırma (enum) üzerindeki `when` `else` olmadan derlenir mi; ve `Boolean` üzerindeki `when` iki değerle kapanır mı.

```kotlin
// Main.kt — when: cok degerli dal, aralik ve numaralandirma
enum class Level { LOW, MEDIUM, HIGH }

fun classify(n: Int): String = when (n) {
    0 -> "sifir"
    1, 2, 3 -> "az"
    in 4..9 -> "orta"
    !in 0..99 -> "aralik disi"
    else -> "cok"
}

fun weight(level: Level): Int = when (level) {
    Level.LOW -> 1
    Level.MEDIUM -> 5
    Level.HIGH -> 10
}

fun main() {
    println(listOf(0, 2, 7, 50, 120).map { classify(it) })
    println(Level.entries.map { weight(it) })
    val flag = true
    println(when (flag) {
        true -> "evet"
        false -> "hayir"
    })
}
```

```
[sifir, az, orta, cok, aralik disi]
[1, 5, 10]
evet
```

Girdiler 0, 2, 7, 50 ve 120 için sonuç `sifir`, `az`, `orta`, `cok`, `aralik disi` oldu. 2 değeri `1, 2, 3` dalına, 7 ise `in 4..9` dalına düştü. 50 değeri aralık dışı dalını tutturmadı ve `else` dalına gitti, 120 ise aralık dışı dalını tuttu. Kural şudur: dallar yukarıdan aşağıya sınanır ve ilk tutan çalışır. Aralık dışı dalı `else` dalından önce yazıldığı için 120 değeri ona ulaştı; `else` dalı daha önce yazılsaydı `aralik disi` dalına hiç ulaşılamazdı; bu ters sıra derlenmediği için ölçülemedi.

İkinci satır `[1, 5, 10]` verdi. `weight` fonksiyonu `else` dalı taşımıyor ve yine de derleniyor: derleyici `Level` numaralandırmasının üç değerini de dalların kapsadığını görür. Sık yapılan hata, numaralandırmaya yeni bir değer ekleyip `when` ifadelerini güncellemeyi unutmaktır; `else` dalı olmayan ifade bu durumda derleme hatası verir ve eksik dalı bulur. `else` yazılırsa bu güvence kaybolur. Bu davranış `when` ifade olarak kullanıldığı için geçerlidir. Java'da `switch` ifadesi de numaralandırmada kapsayıcılık denetler, ama eski `switch` deyimi denetlemez.

Üçüncü satır `evet` verdi: `Boolean` iki değerli bir tip olduğundan `true` ve `false` dalları `else` olmadan kapsayıcıdır.

**DT35.** Kapsayıcılık `enum class` ve `Boolean` ile ölçüldü; mühürlü sınıf üzerindeki `when` denetimi bu derste ölçülmedi.

## Boş Aralık, Adım ve Etiketli break

Aralıklar kolay görünür ve sınır durumları nedeniyle hata üretir. Aşağıdaki blok boş bir aralığı sayar, `step` kullanan bir `downTo` ve `..` kurar, sıfır adımı dener ve iç içe döngüden etiketli `break` ile çıkar.

```kotlin
// Main.kt — bos aralik, adim ve etiketli break
fun main() {
    println((5..1).count())
    println((1..0).toList())
    println((10 downTo 1 step 3).toList())
    println((1..10 step 4).last)
    try {
        println((1..3 step 0).toList())
    } catch (e: IllegalArgumentException) {
        println(e.message)
    }
    var visited = 0
    outer@ for (row in 1..3) {
        for (col in 1..3) {
            if (row * col == 4) break@outer
            visited++
        }
    }
    println(visited)
}
```

```
0
[]
[10, 7, 4, 1]
9
Step must be positive, was: 0.
4
```

`(5..1).count()` `0` verdi ve `(1..0).toList()` boş liste verdi: `..` işleci küçükten büyüğe çalışır, başlangıç bitişten büyükse aralık boştur ve döngü hiç dönmez. Geriye sayılacaksa `downTo` yazılmalıdır. Sık yapılan hata budur: `for (i in 5..1)` yazan kişi geri sayım bekler, program sessizce hiçbir şey yapmaz.

`10 downTo 1 step 3` listesi `[10, 7, 4, 1]` oldu. `1..10 step 4` aralığının `last` değeri `9` çıktı, yani 10 değil: adım üst sınıra tam oturmazsa son değer sınırın altında kalır. Sıfır adım ise `Step must be positive, was: 0.` iletisiyle `IllegalArgumentException` fırlattı; adım pozitif olmak zorundadır, geriye sayma `downTo` ile yapılır, negatif adımla değil.

Son satır `4` verdi. İç döngüde `row * col == 4` olduğunda `break@outer` hem iç hem dış döngüyü bitirdi. Etiketsiz `break` yalnız en içteki döngüyü bitirirdi. Ziyaret sayısı, satır 1'de üç, satır 2'de bir ve satır 2'nin ikinci sütununda kırılma olduğundan toplam dörttür. Java'da etiketli `break` aynı biçimde çalışır; sözdizimi `outer:` ile `break outer;` şeklindedir.

**DT36.** Aralık ölçümleri `Int` ile yapıldı; `Long` aralığında adım ve taşma davranışı bu derste ölçülmedi.

## try Bir İfadedir

Kotlin'de `try` de değer üretir. Aşağıdaki blok `try` ifadesinin değerini bir fonksiyonun dönüşü yapar, `finally` bloğunun ne zaman çalıştığına bakar ve bir `if` ifadesinin içinde fonksiyon çağrısını kullanır. Son olarak `firstOrNull` ile elvis işleci birlikte denenir.

```kotlin
// Main.kt — try bir ifadedir
fun parse(text: String): Int = try {
    text.toInt()
} catch (e: NumberFormatException) {
    -1
} finally {
    println("denendi: $text")
}

fun main() {
    println(parse("42"))
    println(parse("kotlin"))
    val label = if (parse("7") > 5) "buyuk" else "kucuk"
    println(label)
    val firstNegative = listOf(3, -2, 5).firstOrNull { it < 0 } ?: 0
    println(firstNegative)
}
```

```
denendi: 42
42
denendi: kotlin
-1
denendi: 7
buyuk
-2
```

İlk çağrı `parse("42")` önce `finally` bloğunun çıktısı olan `denendi: 42` satırını, sonra dönüş değeri `42` satırını bastı: `finally`, değer çağırana ulaşmadan önce çalışır. İkinci çağrıda `"kotlin"` sayıya çevrilemediği için `catch` dalı çalıştı ve `-1` döndü; yine `denendi: kotlin` önce basıldı. Üçüncü satırda `parse("7")` çağrısı `if` içinde kullanıldı: `denendi: 7` basıldı ve 7 değeri 5'ten büyük olduğu için `buyuk` yazıldı.

Sık yapılan hata, `finally` bloğuna değer döndüren bir ifade koyup bunun try ifadesinin değerini değiştireceğini sanmaktır. Burada `println` son ifade olsa da `finally` bloğunun değeri ifadenin değerine katılmaz; döndürülen değer `try` ya da `catch` bloğunun son ifadesidir. Son satır `-2` verdi: listede `it < 0` koşulunu sağlayan ilk öğe `-2` idi; hiçbiri olmasaydı elvis işleci `0` döndürürdü, ama bu dal ölçülmedi. Java'da `try` bir deyimdir ve değer üretmez.

**DT37.** `try` ölçümleri `NumberFormatException` ile yapıldı; `finally` bloğunda `return` yazılması halinde değerin nasıl ezildiği bu derste ölçülmedi.

## Ölçümlerden Çıkan Alışkanlıklar

Akış denetiminde ölçümlerin ortak dersi, sınır durumlarının sessiz olmasıdır. Boş aralık hata vermez, yalnız hiçbir şey yapmaz. Adım değeri `1..10 step 4` gibi sınıra tam oturmazsa son değer sınırın altında kalır. Sıfır adım ise tek bir iletiyle istisna verir. Bu üç durumdan yalnız sonuncusu hata olarak görünür; ötekiler yanlış çıktı üretir.

`when` ifadesinde `else` dalının bulunmaması bir tasarım aracıdır. Numaralandırmada `else` yazmazsanız, yeni bir değer eklendiğinde derleyici sizi bütün `when` ifadelerine yönlendirir. `else` yazarsanız bu uyarıyı kendiniz kapatmış olursunuz. Bu nedenle `else` dalı, gerçekten "geri kalan her şey" anlamına gelen yerlerde, örneğin tam sayı aralıklarında kullanılmalıdır; kapalı bir değer kümesinde kullanılmamalıdır. Sıra konusunda da dikkat gerekir: dallar yukarıdan aşağıya sınandığı için özel dal genel daldan önce yazılır, ölçümde aralık dışı dalı `else`'ten önce olduğu için 120 değeri ona ulaştı.

`try` ifadesinde `finally` bloğunun değeri sonuca katılmaz, ama yan etkisi her zaman çalışır. Kaynak kapatma ve günlük yazma gibi işler bu yüzden `finally` içinde yapılır, değer hesabı ise `try` ve `catch` içinde. Java'da `try` bir deyimdir; değeri bir değişkene atamak için dışarıda bir değişken tanımlamak gerekir. Kotlin'de `val result = try { ... } catch ...` yazımı bu değişkeni yeniden atanamaz bir `val` yapar ve daha kısa bir kod verir.

Etiketli `break` iç içe döngülerden çıkmak için en okunur yöntemdir, ama kullanımı sınırlı tutulmalıdır. Çoğu durumda aramayı ayrı bir fonksiyona taşıyıp `return` ile çıkmak, etiket gerektirmez ve test edilmesi de kolaydır.

## Sınırlayıcı Ölçüm

Kimlik ölçütü yalnız `==` ile eşleştirilen dallar için geçerlidir. `is Box` dalı tutar, çünkü tip denetimi bir eşitlik sorusu değildir. Yani aynı `when` içinde iki dal yan yana durabilir: biri kimlik yüzünden tutmaz, ötekisi tip yüzünden tutar. Bir `Box(1)` örneğini ikinci kez kurup `when` içinde eşleştirmeye çalışan program, `Box` bir veri sınıfı olmadığı sürece dalı hiç tutturamaz; ama `is Box` yazıldığında dal tutar.

İkinci sınır aralıklardadır: `1..3` kimlik ölçütüne düşmez. İki aralık nesnesi ayrı kurulmuş olsa da eşittir. Bir aralığın eşitliği ile `in` dalının tutması farklı sorulardır: ilki bu bölümde, ikincisi `when` bölümünde ölçüldü.

## Özet

- `if` ve `when` değer üreten ifadelerdir; `when` ifade olarak kullanıldığında bütün durumlar kapsanmalıdır; bunu `else` dalı ya da numaralandırma, `Boolean` veya mühürlü sınıf öznesinde tüm durumların sayılması sağlar.
- `is` bir tip denetimidir ve dal içinde değeri o tipin değişkeni olarak kullanmaya izin verir.
- `..`, `until`, `downTo` ve `step` aralıkları kurar; `for` bu aralıkları dolaşır.
- `when (value)` içindeki bir dal `value == dal` ile eşleşir; sıradan sınıf örneği kimliğe dayandığı için dalı tutturamaz.
- Bu dersin ölçütü: **kimlik**; sınırlayıcı ölçüm: `is Box` dalı tutar ve iki `1..3` aralığı içerik ölçütüne düşer.

## Sonraki Adım

Akışı gördük; şimdi akışı paketlemenin aracına geçiyoruz: fonksiyonlar. Varsayılan değerli parametreler, adlandırılmış argümanlar ve `vararg` ile başlıyoruz. Aynı fonksiyonu iki kez referans olarak almak, aynı nesneyi mi verir, yoksa içerik mi, onu ölçeriz.
