---
title: 'Kapsam Fonksiyonları'
source: 'https://academia.sh/tr/kurslar/kotlin/kapsam-fonksiyonlari'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:40+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - '`let`, `run`, `with`, `apply` ve `also` fonksiyonlarını alıcı ve dönüş değeri açısından ayırt etmek'
    - 'Bir ifadenin hangi nesne üzerinde çalıştığını (`this` ya da `it`) okuyabilmek'
    - 'Kapsam fonksiyonunun döndürdüğü nesnenin eşitlik ölçütünü değiştirmediğini ölçmek'
---

# Kapsam Fonksiyonları

`let`, `run`, `with`, `apply` ve `also` kapsam fonksiyonlarını tanıtır; alıcının `this` mi `it` mi olduğunu ve dönüş değerini ölçer, sonucun eşitlik ölçütünü değiştirmediğini gösterir.

Önceki derste lambdaları değer olarak taşıdık. Kapsam fonksiyonları (scope functions) bu lambdaları bir nesnenin bağlamında çalıştıran beş hazır yüksek mertebeden fonksiyondur: `let`, `run`, `with`, `apply` ve `also`. Aralarındaki fark iki sorudan çıkar: nesneye lambdanın içinde nasıl ulaşılır ve fonksiyon ne döndürür? Dersin sonunda bir üçüncü soru geliyor: kapsam fonksiyonundan geçen bir nesnenin eşitlik ölçütü değişir mi?

## Alıcı ve Dönüş Değeri

Bir kapsam fonksiyonu, üzerinde çağrıldığı nesneyi lambdaya iki yoldan verir. `let` ve `also` nesneyi lambda parametresi `it` olarak verir. `run`, `with` ve `apply` ise nesneyi alıcı (receiver) yapar: lambdanın içinde nesnenin üyelerine `this` ile, çoğunlukla da hiçbir önek yazmadan ulaşılır. Bu ayrım, bir ifadenin hangi nesne üzerinde çalıştığını okurken ilk bakılacak yerdir.

Dönüş değeri iki biçimdedir. `let`, `run` ve `with` lambdanın son ifadesinin sonucunu döndürür. `apply` ve `also` ise nesnenin kendisini döndürür. Yani `apply` bir nesneyi kurarken, `also` nesneye dokunmadan yan bir iş yaparken kullanılır.

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

Bu seçim bir okunabilirlik kararıdır. Lambdanın içinde nesnenin üyelerini önek yazmadan kullanmak istiyorsanız alıcı yazımlı üç fonksiyondan biri uygundur; nesneyi bir ad altında başka bir şeye argüman olarak geçirecekseniz `it` yazımlı ikisi uygundur. Dönüş değeri ise bloktan sonra elinizde ne kalacağına karar verir: nesnenin kendisi mi, hesaplanmış bir sonuç mu.

Aşağıdaki program beş fonksiyonu sırayla çalıştırır. `Point` iki değiştirilebilir alanı olan bir veri sınıfıdır.

```kotlin
// Main.kt — let, run, with, apply ve also
data class Point(var x: Int, var y: Int)

fun main() {
    val letResult = Point(1, 2).let { it.x + it.y }
    val runResult = Point(3, 4).run { x * y }
    val withResult = with(Point(5, 6)) { "$x,$y" }
    val applied = Point(0, 0).apply {
        x = 7
        y = 8
    }
    val alsoResult = Point(9, 9).also { println("gozlem: ${it.x}") }
    println(letResult)
    println(runResult)
    println(withResult)
    println(applied)
    println(alsoResult)
}
```

```
gozlem: 9
3
12
5,6
Point(x=7, y=8)
Point(x=9, y=9)
```

İlk satır `also` içindeki `println` çağrısından geldi ve `gozlem: 9` yazdı; `also` bloğu nesneyi gözlemledi ve nesne olduğu gibi döndü. `let` bloğunda iki alan `it` üzerinden toplandı ve `3` çıktı. `run` bloğunda `x * y` alıcıyla, önek olmadan yazıldı ve `12` çıktı. `with` de aynı alıcı yazımını kullandı ve lambdanın sonucu olan `"5,6"` metnini verdi. `apply` bloğunda alanlar atandı ve fonksiyon nesnenin kendisini döndürdü: `Point(x=7, y=8)`. Son satır `also`ın döndürdüğü nesneyi gösterir: `Point(x=9, y=9)`.

**DT52.** Beş fonksiyon yalnız `Point` üzerinde ve yalnız tam sayı alanlarla ölçüldü; boş değer alabilen alıcı üzerindeki `?.let`, `?.apply` ve `takeIf` kullanımı dersin ilerleyen bölümünde yalnız `String?` ile ölçülür.

**DT53.** `with` bir uzantı değil, nesneyi argüman olarak alan sıradan bir fonksiyondur; bu ayrım burada yalnız söylendi, ölçülmedi.

## Kapsam Fonksiyonu Ölçütü Değiştirmez

Ölçüm şunu sorar: bir nesneyi kapsam fonksiyonundan geçirmek, ikizinin eşit sayılıp sayılmayacağını değiştirir mi? Burada `Point` değişmeyen alanlı bir veri sınıfıdır. Üç gözlem yazıyoruz. `let-copy`: `Point(1, 2).let { it.copy() }`. `run-copy`: `Point(1, 2).run { copy() }`; aynı kopyalama, bu sefer alıcı yazımıyla. `also-ikiz`: aynı `source` değişkeni `also { }` ile geçirilir ve kendisiyle karşılaştırılır.

`let` ve `run` lambdanın sonucunu döndürdüğü için döndürülen şey `copy()` ile kurulan ayrı bir nesnedir. `also` ise kendi nesnesini döndürür.

```kotlin
// Main.kt — kapsam fonksiyonunun dondurdugu
data class Point(val x: Int, val y: Int)

fun main() {
    val source = Point(1, 2)
    val checks = listOf(
        Criterion("let-copy", Point(1, 2), Point(1, 2).let { it.copy() }, Point(1, 3)),
        Criterion("run-copy", Point(1, 2), Point(1, 2).run { copy() }, Point(1, 3)),
        Criterion("also-ikiz", source, source.also { }, Point(1, 3)),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
let-copy: ayni-nesne=false, ikiz=true, farkli=false -> icerik
run-copy: ayni-nesne=false, ikiz=true, farkli=false -> icerik
also-ikiz: ayni-nesne=true, ikiz=true, farkli=false -> tekil
tekil: 1
kimlik: 0
icerik: 2
kismi-icerik: 0
```

`let-copy` ve `run-copy` içeriktir: `ayni-nesne=false`, `ikiz=true`. Kapsam fonksiyonundan çıkan kopya, kaynakla aynı nesne değildir ama aynı değerleri taşıdığı için veri sınıfının `equals`'i onları eşit sayar. Hangi kapsam fonksiyonunun kullanıldığı ölçütü belirlemedi; ölçütü belirleyen `Point`ın veri sınıfı olmasıdır.

`also-ikiz` tekildir: `ayni-nesne=true`. `also` nesnenin kendisini döndürdüğü için ikiz ile birinci değer aynı nesne çıktı. Özet satırları bir tekil, sıfır kimlik, iki içerik ve sıfır kısmi içerik verir.

**DT54.** `also-ikiz` gözleminde ikiz kurulmadı; kapsam fonksiyonu nesnenin kendisini döndürdüğü için karşılaştırma bir nesnenin kendisiyle yapıldı. Bu gözlem ölçüm koşulunu bozar, aşağıda açıklanıyor.

**DT55.** `copy()` çağrısı yüzeysel kopya üretir; iç içe nesne taşıyan bir veri sınıfında kopyanın nasıl davrandığı bu derste ölçülmedi.

## Beş Fonksiyon Neyi Döndürür

Kapsam fonksiyonları arasındaki asıl fark ikiye iner: nesneye `it` ile mi `this` ile mi erişilir ve ifade neyi döndürür. Aşağıdaki blok aynı `Point` nesnesi üzerinde beşini çalıştırır ve dönen nesneyi `===` ile kaynağa karşı sınar.

```kotlin
// Main.kt — kapsam fonksiyonlari neyi dondurur
data class Point(var x: Int, var y: Int)

fun main() {
    val p = Point(1, 2)
    println(p.let { it } === p)
    println(p.run { this } === p)
    println(with(p) { this } === p)
    println(p.apply { x = 10 } === p)
    println(p.also { it.y = 20 } === p)
    println(p.let { 5 })
    println(p.apply { x = 11 }.run { x + y })
    println(p)
}
```

```
true
true
true
true
true
5
31
Point(x=11, y=20)
```

Beş satırın hepsi `true` verdi, ama beşi aynı şey için değil. `p.let { it } === p` ve `p.run { this } === p` doğrudur çünkü lambdanın son ifadesi nesnenin kendisidir. `with(p) { this } === p` aynı sebeple doğrudur. `apply` ve `also` ise lambdanın değerine bakmaz, her zaman alıcı nesneyi döndürür.

Altıncı satır `5` verdi: `let { 5 }` ifadesinin değeri lambdanın son ifadesidir, nesne değil. Yedinci satır `31`: `apply { x = 11 }` nesneyi döndürdü, `run { x + y }` ise toplamı hesapladı, 11 ile 20'nin toplamı 31. Son satır `Point(x=11, y=20)` verdi, yani önceki beş çağrıdaki atamalar nesnenin kendisini değiştirdi. Bu atamalar yerel bir kopyada değil, `p` üzerinde yapılmıştı.

Kural: `let`, `run` ve `with` lambdanın sonucunu verir; `apply` ve `also` nesneyi verir. Sık yapılan hata, `apply` içinde son satıra bir değer yazıp onun döneceğini sanmaktır. Java'da buna denk bir yapı yoktur; zincirleme için yöntemlerin `this` döndürmesi (builder kalıbı) gerekir.

**DT56.** Dönüş değerleri `Point` ve tam sayı sonuçlarla ölçüldü; `with` bir uzantı değil sıradan bir fonksiyondur, ama bu ayrım ölçülmedi.

## Null ile Kapsam Fonksiyonları ve takeIf

En sık kullanım `?.let` ile boş olabilecek bir değeri güvenle işlemektir. Aşağıdaki blok bunu dolu ve boş bir `String?` ile dener, `?.let` ile `.let { it?.... }` arasındaki farka bakar ve `takeIf` ile `takeUnless` fonksiyonlarını sınar.

```kotlin
// Main.kt — null ile kapsam fonksiyonlari ve takeIf
fun main() {
    val present: String? = "kotlin"
    val missing: String? = null
    println(present?.let { it.length })
    println(missing?.let { it.length })
    println(missing.let { it?.length })
    println(missing?.apply { length })
    println(missing?.let { it.length } ?: -1)
    println(present.takeIf { it?.startsWith("k") == true })
    println(present.takeUnless { it?.startsWith("k") == true })
    println("abc".takeIf { it.length > 5 })
}
```

```
6
null
null
null
-1
kotlin
null
null
```

`present?.let { it.length }` `6` verdi; `missing?.let { it.length }` ise `null`. Güvenli çağrı `?.` alıcı boşsa lambdayı hiç çalıştırmadı. Üçüncü satır da `null` verdi ama sebebi farklıdır: `missing.let { it?.length }` ifadesinde `let` boş değer üzerinde de çağrılır, lambda çalışır ve içeride `it?.length` boş sonuç üretir. Bu iki biçimin sonucu aynı olsa bile lambda birincisinde çalışmadı, ikincisinde çalıştı. Dördüncü satır `null`: `missing?.apply { length }` de alıcı boş olduğu için hiçbir şey çalıştırmadı, sonuç boş değerdir. Beşinci satır `-1`: elvis işleci boş sonucu varsayılana çevirdi.

`takeIf` koşul doğruysa nesneyi, yanlışsa `null` döndürür. `present.takeIf { ... }` `kotlin` verdi, çünkü dizgi `k` ile başlıyor. `takeUnless` ters çalışır ve aynı koşulla `null` verdi. `"abc".takeIf { it.length > 5 }` `null` verdi, çünkü koşul sağlanmadı. Sık yapılan hata, `takeIf` sonucunu boş olabilir tipte olduğunu unutarak doğrudan kullanmaktır; derleyici bu durumda derleme hatası verir, ama bu hata ölçülmedi.

**DT57.** Boş değer ölçümü `String?` ile yapıldı; `takeIf` koşulunun yan etki taşıması ya da `takeIf` zincirleri bu derste ölçülmedi.

## İç İçe Kapsam Fonksiyonları ve this

İki kapsam fonksiyonu iç içe yazıldığında `this` ve `it` hangi nesneye bakar? Aşağıdaki blok bir `run`'ın içinde ikinci bir `run` çalıştırır, dıştaki alıcıya etiketle erişir, ardından `run` ile nesnesiz bir blok kullanır ve iç içe `apply` ile `size` okunur.

```kotlin
// Main.kt — ic ice kapsam fonksiyonlari ve this
data class Box(var size: Int)

fun main() {
    val box = Box(1)
    val result = box.run outer@{
        size = 5
        Box(size * 2).run { "ic: $size, dis: ${this@outer.size}" }
    }
    println(result)
    val note = run {
        val temp = 21
        temp * 2
    }
    println(note)
    Box(3).apply {
        Box(4).apply {
            println(size)
        }
        println(size)
    }
}
```

```
ic: 10, dis: 5
42
4
3
```

İlk satır `ic: 10, dis: 5` verdi. Dış `run`'da `size = 5` atandı, iç `run`'da `size * 2` ile yeni bir `Box(10)` kuruldu. İç blokta `size` en yakın alıcıya, yani `Box(10)` nesnesine bakar. Dış alıcıya erişmek için `this@outer.size` yazılır ve `5` okunur. İç içe alıcı kullanımı bu yüzden okunması zor kod üretir; `also` ve `let` ile `it` adını vermek daha açıktır. Sık yapılan hata, iç blokta `size` yazıp dış nesnenin alanını okuduğunu sanmaktır.

İkinci satır `42` verdi: nesnesiz `run { }` yalnız bir blok açıp son ifadeyi döndürür. Üçüncü ve dördüncü satırlar `4` ve `3` verdi: iç `apply` içinde `size` iç nesnenin 4 değeridir, dış `apply` içinde ise dış nesnenin 3 değeri. İç blok bitince kapsam dışa döner. Java'da aynı etkiyi iç sınıflarda `Dis.this.alan` yazımı verir; Kotlin'de etiket bunun karşılığıdır.

**DT58.** İç içe alıcı ölçümü iki düzeyle yapıldı; üç ya da daha derin iç içe kullanımda etiketsiz `this` çözümü bu derste ölçülmedi.

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

Kapsam fonksiyonlarını seçerken iki soruya bakılır: nesneye `it` mi `this` mi ile erişilecek ve sonuç nesne mi lambdanın değeri mi olacak. Ölçüm tabloyu doğruladı: `apply` ve `also` nesneyi döndürür, `let`, `run` ve `with` lambdanın değerini. Pratikte `apply` nesne kurarken, `also` yan etki (günlük yazma gibi) eklerken, `let` boş olabilecek değeri dönüştürürken, `run` ve `with` ise nesne üzerinde bir hesap yapıp sonuç alırken kullanılır. Bu eşleşme bir kural değil, yaygın bir alışkanlıktır; aynı iş çoğu zaman başka bir fonksiyonla da yapılabilir.

Aşırı kullanım gerçek bir risktir. İç içe üç kapsam fonksiyonu, `this` ve `it` adlarının hangi nesneye gittiğini izlemeyi zorlaştırır. Ölçümde iç `run` içindeki `size` değeri iç nesneye, dış değere erişim ise `this@outer.size` ile yapıldı. İç içe iki düzeyi aşan kullanımda adlandırılmış yerel değişkenler daha açıktır. Kural olarak, bir kapsam fonksiyonunun içine bir başkası yazılıyorsa, ya dıştaki `it` ile adlandırılmalı ya da kod ayrı bir fonksiyona taşınmalıdır.

Boş değerlerle `?.let` yaygın kalıptır ve ölçüm iki biçim arasındaki farkı gösterdi: `missing?.let { ... }` lambdayı hiç çalıştırmaz, `missing.let { it?.... }` ise lambdayı boş değerle çalıştırır. Sonuçlar aynı görünse de yan etki taşıyan bir lambdada fark ortaya çıkar. `takeIf` ve `takeUnless` ise bir koşulu zincire sokar: koşul sağlanmazsa `null` döner ve sonraki `?.` adımları atlanır.

Bir diğer sık hata, `apply` bloğunun son satırına bir değer yazıp onun döneceğini sanmaktır. `apply` her zaman nesneyi döndürür, bu yüzden sondaki ifade göz ardı edilir. Sonucu başka bir değere çevirmek istiyorsanız `let` ya da `run` seçilmelidir. Java'da bu işlerin karşılığı builder kalıbıdır ve her yöntemin `this` döndürmesine dayanır; Kotlin bunu dil düzeyinde, mevcut sınıfı değiştirmeden sağlar.

Son olarak, `with` bir uzantı değil, nesneyi argüman alan sıradan bir fonksiyondur. Bu yüzden boş olabilecek bir nesneye `?.` ile bağlanamaz; boş değerli alıcıda `?.run` ya da `?.let` seçilmelidir.

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

`also-ikiz` gözlemi ölçüm koşulunu bozar: `Criterion` bir değerin ayrı kurulmuş ikizini bekler, oysa `also` aynı nesneyi geri verir. Çıkan tekil sonuç ölçütün bir bulgusu değil, fonksiyonun dönüş değeri kuralının yansımasıdır. Bu yüzden kapsam fonksiyonunun ölçütü değiştirmediğini göstermek için `let` ve `run` gözlemleri anlamlıdır, `also` gözlemi ise yalnız sınırı gösterir. Bir ifadede kapsam fonksiyonu görürseniz şu iki soruyu sorun: bu ifade hangi nesne üzerinde çalışıyor ve fonksiyon bu nesneyi mi yoksa lambdanın sonucunu mu döndürüyor? Eşitlik sorusunun yanıtı hep nesnenin kendi tipinde kalır.

## Özet

- `let` ve `also` nesneyi `it` olarak, `run`, `with` ve `apply` alıcı olarak (`this`) verir.
- `let`, `run` ve `with` lambdanın sonucunu, `apply` ve `also` nesnenin kendisini döndürür.
- `Point(1, 2).let { it.copy() }` ve `run { copy() }` ayrı ama eşit nesne verdi: içerik.
- `also` aynı nesneyi geri verdiği için `also-ikiz` tekil çıktı ve ölçüm koşulunu bozdu.
- Bu dersin ölçütü: **içerik**; sınırlayıcı ölçüm: `also` ikizi tekildir ve ikiz kurmadığı için ölçüm koşulunu bozar.

## Sonraki Adım

Dil temelleri burada kapanıyor. Sonraki konuda eşitlik sorusuna `null` giriyor: bir değer hiç olmayabildiğinde `null` ile `null` aynı mıdır ve derleyici bu durumu tipin içinde nasıl izler? Konu, `Null Güvenliği` dersiyle başlıyor.
