---
title: 'Dizgi Şablonları'
source: 'https://academia.sh/tr/kurslar/kotlin/dizgi-sablonlari'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:40+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - '`$ad` ve `${ifade}` şablon biçimlerini ve kaçış gerektiren durumu yazmak'
    - 'Üç tırnaklı ham dizgiyi ve `trimIndent` fonksiyonunu kullanmak'
    - 'Şablonla kurulan bir dizginin eşitlik ölçütünü ve bunun tek değişkenli şablonda nasıl değiştiğini açıklamak'
---

# Dizgi Şablonları

Dizgi şablonlarını, ham dizgileri ve `trimIndent` fonksiyonunu tanıtır; şablonla kurulan dizginin içerik ölçütüne düştüğünü, tek değişkenli şablonun ise tekil kaldığını ölçer.

Sayı tiplerinde otomatik kutulama, aynı değerin iki nesneye bölünebileceğini gösterdi. Dizgiler bu soruyu başka bir yerden sorar: bir metni şablonla kurduğumda, yazdığım metin ile aynı nesneyi mi elde ederim? Önce şablonun kendisine bakıyoruz: nasıl yazılır, ham dizgiyle nasıl birleşir ve çıktıda neyi değiştirir. Ardından aynı şablonları eşitlik ölçüsünden geçiriyoruz ve tek bir yazım biçiminin iki ölçüte düştüğü yeri arıyoruz.

## Şablon, Ham Dizgi ve trimIndent

Kotlin'de çift tırnaklı bir dizgi içine değer yerleştirmek için `$` işareti kullanılır. `$ad` bir değişkenin değerini, `${ifade}` ise bir ifadenin sonucunu dizgiye koyar. İfade biçimi bir fonksiyon çağrısı ya da bir özellik erişimi de olabilir: `${items.size}`, `${items.first().uppercase()}`. Dizgi içinde `$` karakterini kendi anlamıyla yazmak isterseniz önüne ters bölü koyarsınız: `\$5`.

Üç çift tırnakla başlayan dizgi ham dizgidir (raw string). Satır sonları ve girintiler olduğu gibi korunur, kaçış dizileri çalışmaz, ama şablonlar çalışır. Ham dizgi çok satırlı olduğunda her satırın başındaki girinti metnin parçası olur; `trimIndent` fonksiyonu ortak girintiyi siler ve baştaki ile sondaki boş satırı atar.

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 bu üç şeyi birlikte çalıştırır.

```kotlin
// Main.kt — sablonlar, ham dizgi ve trimIndent
fun main() {
    val name = "Zeynep"
    val items = listOf("elma", "armut")
    println("Merhaba, $name")
    println("Oge sayisi: ${items.size}, ilki: ${items.first().uppercase()}")
    println("Fiyat: \$5")
    val text = """
        ad: $name
        ogeler: ${items.joinToString()}
    """.trimIndent()
    println(text)
}
```

```
Merhaba, Zeynep
Oge sayisi: 2, ilki: ELMA
Fiyat: $5
ad: Zeynep
ogeler: elma, armut
```

İlk satır tek bir değişkeni yerleştirir: `Merhaba, Zeynep`. İkincisi iki ifade yerleştirir: `items.size` listenin uzunluğunu verir, `items.first().uppercase()` ilk elemanı büyük harfe çevirir; çıktıda `Oge sayisi: 2, ilki: ELMA` görünür. Üçüncü satırda ters bölü, `$5` metnini şablon olarak yorumlanmaktan kurtarır ve `Fiyat: $5` basılır. Ham dizgi dört satırlık kaynak koddan iki satırlık bir metin üretir: girinti `trimIndent` ile silinir ve çıktıda `ad: Zeynep` ile `ogeler: elma, armut` satırları hizalı görünür. `items.joinToString()` eleman listesini virgülle ayırarak tek metne çevirir.

Programdaki üç yazım biçimi üç ayrı iş görür. Düz `$ad` en kısa yazımdır ve yalnız bir adın değerini koyar. Süslü parantezli biçim, ad tek başına yetmediğinde ya da şablonun hemen ardından harf geldiğinde gerekir; çünkü parantez, adın nerede bittiğini açıkça gösterir. Ham dizgi ise kaçış dizilerinden kaçınılması gereken çok satırlı metinlerde, örneğin bir yapılandırma örneğinde ya da biçimli bir ileti taslağında işe yarar. `trimIndent` kaynak kodu girintili tutmanıza izin verir; çıktıdaki metin ise sol kenara yaslı kalır.

**DT24.** Şablon içinde yalnız basit ifadeler ölçüldü; iç içe şablon ya da dizgi içinde dizgi içeren bir ifadenin davranışı bu dersin dışındadır.

**DT25.** `trimIndent` ortak girintiyi siler; girintisi düzensiz satırlarda ne yaptığı bu derste ölçülmedi.

## Şablonla Kurulan Dizginin Ölçütü

Dersin sorusuna dönüyoruz. Önceki derste aynı dizgi sabiti iki değişkende tek nesne oldu ve tekil çıktı. Şablonla kurulan dizgi için üç gözlem yazıyoruz. `sablon-ile-kurulan`: `"dil: $name"` ile `"dil: $name"`, yani iki ayrı şablon ifadesi. `tek-degiskenli-sablon`: yalnız `"$name"` biçiminde, başka metin içermeyen şablon, `name` değişkenine karşı. `sablon-ve-ek`: `"$name."`, yani değişken artı sabit bir ek.

İkinci derste `joinToString` ve `buildString` ile kurulan dizgilerin içerik çıktığını görmüştük. Şablon da çalışma anında bir dizgi kuran bir yazım biçimidir; bu yüzden aynı sonucu bekleriz. Beklentiyi ölçüyoruz, çünkü şablonun derleyici tarafından nasıl işlendiği bir dil kuralı değil, bir uygulama ayrıntısıdır ve ayrıntı yazıdan çıkarılamaz. Her gözlemde ikiz, aynı şablonun ikinci kez yazılmasıyla ya da sabitle elde edilir; farklı değer ise `kotlim` harf değişikliğini taşır.

```kotlin
// Main.kt — sablonla kurulan dizgi
fun main() {
    val name = "kotlin"
    val checks = listOf(
        Criterion("sablon-ile-kurulan", "dil: $name", "dil: $name", "dil: kotlim"),
        Criterion("tek-degiskenli-sablon", name, "$name", "kotlim"),
        Criterion("sablon-ve-ek", "$name.", "$name.", "kotlim."),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
sablon-ile-kurulan: ayni-nesne=false, ikiz=true, farkli=false -> icerik
tek-degiskenli-sablon: ayni-nesne=true, ikiz=true, farkli=false -> tekil
sablon-ve-ek: ayni-nesne=false, ikiz=true, farkli=false -> icerik
tekil: 1
kimlik: 0
icerik: 2
kismi-icerik: 0
```

`sablon-ile-kurulan` içeriktir. İki şablon ifadesi iki ayrı dizgi üretir; `ayni-nesne=false`, ama `==` ile eşittirler ve farklı metinle eşit değildirler. `sablon-ve-ek` de içeriktir; bir değişkeni sabit bir ekle birleştiren şablon her çalıştığında yeni bir nesne kurar.

`tek-degiskenli-sablon` ise tekildir: `"$name"` yazıldığında elde edilen nesne, `name` değişkeninin işaret ettiği nesnenin kendisidir, `ayni-nesne=true`. Başka bir metin eklenmeyen şablon, yeni bir dizgi kurmaz.

Bu üç gözlemin ortak noktası, hepsinin aynı `name` değişkenini kullanmasıdır. Değişken tek, şablon biçimi değişiyor. Bu seçim bilinçlidir: ölçütü etkileyen şeyin değişkenin değeri değil, şablonun biçimi olduğunu görmek istiyoruz. `sablon-ve-ek` içindeki nokta, adın hemen ardından gelen sabit bir metindir; şablon bu noktayı adın parçası saymaz, yalnız `name` değişkenini yerleştirir ve noktayı olduğu gibi bırakır.

**DT26.** Bu üç gözlem Kotlin 2.4.21 ile ölçüldü; `"$s"` ifadesinin yeni bir nesne kurmadığı derleyicinin bu sürümdeki davranışıdır ve başka sürümlerde aynı olacağı iddia edilmiyor.

**DT27.** `name` değişkeni burada bir dizgi sabitine bağlı bir `val`; değişkenin çalışma anında kurulmuş bir dizgiye bağlı olduğu hâlde aynı sonucun çıkması ölçülmedi.

Bu sonuç dizgilerle çalışırken bir alışkanlık önerir. Bir metni başka bir metne eşitlik için karşılaştırırken, metnin sabitten mi şablondan mı geldiğini düşünmek gerekmez; `==` her ikisinde de metne bakar. Metnin hangi nesne olduğu ancak `===` sorulduğunda ortaya çıkar ve o zaman da yanıt şablonun içinde başka bir şey olup olmadığına göre değişir.

## Dolar İşareti Ne Zaman Şablon Başlatır

Yukarıda `\$5` ile dolar işaretinin kaçırılabildiğini gördük. Peki her `$` kaçış ister mi? Aşağıdaki blok yedi yazımı arka arkaya basar: rakamla biten bir `$`, boşlukla çevrili bir `$`, dizginin sonundaki bir `$`, iki ayrı kaçış yolu ve değişken adının hemen ardına yazılan nokta.

```kotlin
// Main.kt — dolar isareti ne zaman sablon baslatir
fun main() {
    val name = "kotlin"
    println("fiyat: $5")
    println("a $ b")
    println("son: $")
    println("${'$'}name")
    println("\$name")
    println("$name.length")
    println("${name.length}")
}
```

```
fiyat: $5
a $ b
son: $
$name
$name
kotlin.length
6
```

İlk üç satırda `$` olduğu gibi basıldı: `$5`, `a $ b` ve sonda duran `$`. Kural şudur: `$` işaretinden sonra bir tanımlayıcının ilk harfi gelirse şablon başlar; rakam, boşluk ya da dizgi sonu gelirse `$` sıradan bir karakterdir. Bu yüzden `"fiyat: $5"` kaçış istemeden çalıştı. Dördüncü ve beşinci satır, adın başında `$` isteyen metni yazmanın iki yolunu verir: `"${'$'}name"` ve `"\$name"` aynı `$name` metnini basar.

Altıncı satır sık yapılan hatayı gösterir. `"$name.length"` yazan kişi 6 bekler, ekrana `kotlin.length` çıkar: düz `$ad` biçimi yalnız adı alır, noktadan sonrası metin olarak kalır. Özelliğe erişmek için `"${name.length}"` yazılır ve son satır `6` verir. Mekanizmanın nedeni düz biçimin ifade değil yalnız ad okumasıdır; süslü parantez ifadenin sınırını açıkça belirler.

Java karşılaştırması: Java'da dizgi içine değer koymak için `+` ile birleştirme, `String.format` ya da `formatted` kullanılır; `$` bir özel karakter değildir. Java'dan Kotlin'e geçen biri `$` işaretini her zaman kaçıracağını sanabilir. Bu gözlem yalnız Kotlin tarafını ölçer.

**DT28.** Bu yedi yazım tek bir `name` değişkeniyle ölçüldü; `$` ardından Unicode harf gelen adların nasıl ayrıştığı bu derste ölçülmedi.

## Dizgi Değiştirilemez

Kotlin'de `String` değiştirilemez: hiçbir yöntem var olan nesnenin içeriğini değiştirmez. Değişikliği yapan yöntemler yeni bir dizgi döndürür. Aşağıdaki blok önce `uppercase` ile bunu gösterir, sonra dört yöntemi eşitlik ölçüsünden geçirir. Soru şudur: bir yöntem bir şey değiştirmediğinde yeni bir nesne mi kurar, yoksa eldekini mi döndürür?

```kotlin
// Main.kt — dizgi degistirilemez, yontemler yeni nesne dondurebilir
fun main() {
    val text = "kotlin"
    val upper = text.uppercase()
    println(text)
    println(upper)
    val checks = listOf(
        Criterion("replace-eslesme-yok", text, text.replace("x", "y"), "kotlim"),
        Criterion("replace-eslesme-var", text, text.replace("k", "k"), "kotlim"),
        Criterion("uppercase-lowercase", text, text.uppercase().lowercase(), "kotlim"),
        Criterion("trim-bosluksuz", text, text.trim(), "kotlim"),
    )
    for (check in checks) {
        check.report()
    }
    summarize(checks)
}
```

```
kotlin
KOTLIN
replace-eslesme-yok: ayni-nesne=true, ikiz=true, farkli=false -> tekil
replace-eslesme-var: ayni-nesne=false, ikiz=true, farkli=false -> icerik
uppercase-lowercase: ayni-nesne=false, ikiz=true, farkli=false -> icerik
trim-bosluksuz: ayni-nesne=true, ikiz=true, farkli=false -> tekil
tekil: 2
kimlik: 0
icerik: 2
kismi-icerik: 0
```

İlk iki satır değiştirilemezliği doğrular. `text` hâlâ `kotlin` iken `upper` ayrı bir nesne olarak `KOTLIN` değerini taşır. Ölçüm satırları ise iki ölçüte ayrıldı. `replace-eslesme-yok` tekildir: `"x"` metnini `"y"` ile değiştirmeye çalışan `replace`, eşleşme bulmayınca aynı nesneyi döndürür. `trim-bosluksuz` de tekildir: kırpılacak boşluğu olmayan dizgi için `trim` kendi nesnesini döner. `replace-eslesme-var` içeriktir: eşleşme bulunduğunda, yerine konan metin aynı olsa bile yeni bir dizgi kurulur. `uppercase-lowercase` de içeriktir, çünkü iki dönüşüm ardışık yeni nesneler üretir.

Bu, ikinci derste gördüğümüz kuralı genişletir: ölçütü belirleyen değerin nereden geldiğidir. Burada kaynak bir yöntemdir ve yöntem iş yapmadığında girdisini döndürür. Bu davranış dilin güvencesi değil, ölçülen bir sonuçtur; `===` ile test eden kod bu yüzden kırılgandır. Java'da `String` de değiştirilemez ve benzer biçimde yöntemler yeni dizgi döndürür, ama bu bölümde Java tarafı ölçülmedi.

**DT29.** Dört yöntem tek bir dizgiyle ölçüldü; `trim` dışındaki `trimStart`, `trimEnd` ve `substring` için aynı "iş yoksa aynı nesne" davranışının geçerli olduğu bu derste ölçülmedi.

## Uzunluk, Kaçış ve UTF-16

`length` bir dizgideki karakter sayısını verir, ama "karakter" sözcüğü burada tuzaklıdır. Aşağıdaki blok kaçış dizili bir dizgiyi ham eşiyle karşılaştırır, ardından bir emoji karakterinin uzunluğunu ve kod noktası sayısını sorar. Sonda ham dizginin `trimMargin` ile satır başlarından kırpılması gösterilir.

```kotlin
// Main.kt — uzunluk, kacis dizisi ve UTF-16
fun main() {
    val escaped = "a\nb"
    val raw = """a\nb"""
    println(escaped.length)
    println(raw.length)
    val face = "\uD83D\uDE00"
    println(face.length)
    println(face.codePointCount(0, face.length))
    val margin = """
        |satir bir
        |satir iki
    """.trimMargin()
    println(margin.lines().size)
    println(margin.lines().last())
}
```

```
3
4
2
1
2
satir iki
```

`"a\nb"` dizgisinin uzunluğu `3` çıkar: `\n` tek bir satır sonu karakteridir. Aynı yazım ham dizgide `4` verir, çünkü ham dizgide kaçış çalışmaz ve ters bölü ile `n` ayrı iki karakter olarak sayılır. Kaçışın çalışıp çalışmadığı bu iki sayıdan okunur.

Emoji için `length` `2` verir, `codePointCount` ise `1`. Kotlin dizgileri UTF-16 kodlamasıyla tutulur ve bu karakter iki kod birimine (surrogate pair) ihtiyaç duyar. `length` kod birimi sayar, kod noktası saymaz. Sık yapılan hata, kullanıcıya gösterilen "karakter sayısı" için `length` kullanmaktır; emoji içeren metinde sayı yanlış çıkar. Java'da `String.length()` aynı kod birimi sayısını verir; bu Kotlin dizgisinin altında JVM dizgisi olmasından gelir.

Son iki satır `trimMargin` sonucudur. `|` işaretiyle başlayan iki satırdan iki satır kalır ve ikincisi `satir iki` olarak okunur. `trimIndent` ortak girintiyi siler, `trimMargin` ise her satırda `|` işaretine kadar olan kısmı atar; girintisi düzensiz metinde ikincisi daha öngörülebilirdir. Bu karşılaştırma yalnız bu iki satırlık örnekle ölçüldü.

**DT30.** Uzunluk ölçümü bir emoji ve bir kaçış dizisiyle yapıldı; birleşik karakterler (örneğin harf ile ayrı aksan işareti) için `length` sonucunun ne olduğu bu derste ölçülmedi.

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

Şablon yazımında en sık hata, düz `$ad` biçimini ifade yazımıyla karıştırmaktır. Kural basittir: özelliğe, yönteme ya da herhangi bir işleme ihtiyaç varsa süslü parantez kullanılır. Ölçümde `"$name.length"` çıktısı `kotlin.length` oldu; bu hata derleme sırasında yakalanmaz çünkü yazım geçerlidir ve sonuç yalnızca yanlış görünür. Bu tür hatalar testte değil, çıktıya bakınca fark edilir. Bu yüzden şablonlu mesajlar yazarken, noktadan sonra gelen her şeyin süslü parantezin içinde olması bir alışkanlık hâline getirilmelidir.

Dizgilerin değiştirilemez olması iki sonuç doğurur. Birincisi, `uppercase` gibi bir yöntemin sonucunu bir değişkene atamayı unutmak, değişikliğin kaybolması demektir: `text.uppercase()` satırını tek başına yazan kod hiçbir şey yapmaz. İkincisi, bir döngüde dizgiyi `+` ile büyütmek her adımda yeni bir nesne üretir; çok adımlı birleştirmede `StringBuilder` ya da `buildString` kullanılır. Java'da da aynı kural geçerlidir ve Kotlin burada ayrı bir güvence getirmez.

Uzunluk konusunda ise "karakter" sözcüğüne güvenmemek gerekir. `length` UTF-16 kod birimi sayar; bu yüzden emoji ve bazı yazı sistemlerindeki karakterler iki birim sayılır. Kullanıcıya "en çok 20 karakter" sınırı gösteren bir form, bu sayıyı `length` ile denetlerse emoji kullanan biri için sınırı yanlış uygular. Kod noktası sayısı gerekiyorsa `codePointCount` kullanılır; kullanıcının algıladığı karakter (grafem) sayısı ise ayrı bir kütüphane gerektirir ve bu derste ölçülmedi.

Ham dizgilerde kaçış çalışmaz; bu yüzden dosya yolları, düzenli ifadeler ve çok satırlı metinler için uygundur, ama `\n` yazan kişi satır sonu elde etmez. Ölçümde aynı yazım ham dizgide dört, kaçışlı dizgide üç karakter verdi. Hangisinin kullanıldığını sayıdan okuyabilmek, hata ayıklarken işe yarar.

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

Şablonla kurulan dizginin ölçütü içeriktir, ama bu genelleme tek değişkenli şablonu dışarıda bırakır. `"$name"` yalnız bir değişkeni yerleştirdiğinde tekil çıktı; metin ya da ek geldiği anda içeriğe döndü. Aynı yazım biçimi, yani şablon, iki ölçüte düşebiliyor ve ayrım şablonun içinde bir şeyin daha olup olmadığına bakıyor.

Buradan bir kural çıkarılabilir: dizgileri `===` ile karşılaştırmak yerine `==` kullanın. Sınırlayıcı ölçüm bu kuralın gerekçesini gösterir: aynı şablon biçimi tekil de verebilir, içerik de verebilir; `==` her iki durumda da doğru yanıtı verir.

## Özet

- `$ad` ve `${ifade}` bir değeri dizgiye yerleştirir; dizgide `$` karakterini yazmak için `\$` kullanılır.
- Üç tırnaklı ham dizgi satır sonlarını korur; `trimIndent` ortak girintiyi siler.
- İki ayrı şablon ifadesi aynı metni üretse de ayrı nesnelerdir.
- Yalnız bir değişken yerleştiren `"$name"` şablonu, Kotlin 2.4.21'de, değişkenin kendi nesnesini verir.
- Bu dersin ölçütü: **içerik**; sınırlayıcı ölçüm: tek değişkenli şablon tekildir.

## Sonraki Adım

Dizgileri kurmayı gördük. Sıradaki derste programın akışına geçiyoruz: `if` ve `when` ifadeleri, `for` döngüleri ve aralıklar. `when` bir değeri dalla eşleştirirken `==` kullanır; hangi örneğin dalı tutturamadığını ölçeriz. O derste kimlik ölçütü yeniden karşımıza çıkacak ve `is` denetiminin `==` ile aynı soruyu sormadığını göreceğiz.
