---
title: 'Sınıflar ve Yapıcılar'
source: 'https://academia.sh/tr/kurslar/kotlin/siniflar-ve-yapicilar'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:40+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Özellik başlatıcılarının, init bloğunun ve ikincil yapıcı gövdesinin çalışma sırasını ölçer'
    - 'Varsayılan değerli parametrelerin ve özel erişimci tanımının davranışını gösterir'
    - 'equals yazılmamış bir sınıfın iki yoldan kurulan aynı değerli örneklerinin kimlik ölçütüne düştüğünü ölçer'
    - 'Eşitliği sınıfın değil yazılan equals yöntemlerinin belirlediğini sınırlayıcı ölçümle doğrular'
---

# Sınıflar ve Yapıcılar

Birincil yapıcının, init bloğunun ve ikincil yapıcının çalışma sırasının ve equals yazılmamış bir sınıfın örneklerinin neden kimlik ölçütüne düştüğünün ölçümü.

Önceki ders Elvis işlecinin sağ tarafında `equals`'i yazılmamış bir sınıfın ölçütü kimliğe çevirdiğini gösterdi. Bu ders o sınıfların nasıl yazıldığına bakar: birincil yapıcı, `init` bloğu, ikincil yapıcı ve özellikler. Önce hangisinin ne zaman çalıştığı ölçülür, sonra aynı değerli iki örneğin neden eşit sayılmadığı ölçüt üzerinden açıklanır.

Ölçü ekseni aynı kalır: bir değerin kendisine eşit sayılan başka bir değerle ilişkisi, yani **ölçüt**. `Criterion` sınıfı açılış dersindeki hâliyle aşağıda yeniden verilir. Kural sırayla işler: ilk değer ikiziyle aynı nesneyse tekil; değilse ve ikiz eşit değilse kimlik; ikiz eşit ama üçüncü değer eşit değilse içerik; üçüncü değer de eşitse kısmi içerik. Bu ders kimlik kesitini ölçer ve sınırlayıcı ölçümde içerik ile kısmi içeriğe geçer.

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

## Yapıcıların Çalışma Sırası

Kotlin'de bir sınıfın birincil yapıcısı sınıf başlığına yazılır: `class Member(val name: String, val level: Int)`. Parametrenin önündeki `val` hem parametreyi hem aynı adlı özelliği tanımlar. Gövdede başlatıcılı özellikler ve `init` blokları yer alabilir; bunlar birincil yapıcının gövdesi gibi davranır ve dosyadaki yazılış sırasıyla çalışır. İkincil yapıcı `constructor` anahtar sözcüğüyle yazılır ve `: this(...)` ile birincil yapıcıya yönlenmek zorundadır. Yönlenilen yapıcı önce koşar, ikincil yapıcının kendi gövdesi en sonda.

Aşağıdaki blok bu sırayı çıktıdan okunur hâle getirir. `trace` fonksiyonu her çağrıldığında bir satır basar. `title` özelliğinin başlatıcısı `trace` çağırır, `init` bloğu bir satır basar, ikincil yapıcının gövdesi de bir satır basar. `Member("ada")` ikincil yapıcıyı, `Member("can", 2)` doğrudan birincil yapıcıyı çağırır.

```kotlin
// Main.kt — yapici sirasi
fun trace(label: String): String {
    println("adim: $label")
    return label
}

class Member(val name: String, val level: Int) {
    val title = trace("ozellik")

    init {
        println("init: $name")
    }

    constructor(name: String) : this(name, 1) {
        println("ikincil: $name")
    }
}

fun main() {
    val first = Member("ada")
    println(first.level)
    val second = Member("can", 2)
    println(second.level)
}
```

```
adim: ozellik
init: ada
ikincil: ada
1
adim: ozellik
init: can
2
```

İki kurulumda da `adim: ozellik` satırı `init: ...` satırından önce gelir, çünkü `title` bloktan önce yazılmıştır. İkincil yapıcı yalnızca ilk kurulumda koşar ve `ikincil: ada` satırı `init: ada` satırından sonra basılır: birincil yapıcı ve onun başlatıcıları ikincil yapıcının gövdesinden önce tamamlanır. İkinci kurulumda `ikincil` satırı yoktur, çünkü birincil yapıcı doğrudan çağrılmıştır.

**TN11.** Bu ölçüm yalnızca bu sınıf için sıra gösterir. Başlatıcı ile `init` bloğunun yer değiştirmesi çıktıyı değiştirir; sıra yazılış sırasıdır. Hangi başlatıcının hangi `init` bloğundan önce geleceği, ölçülen sınıfın yazılışına bağlıdır.

## Özellikler, Varsayılan Değerler ve Erişimciler

Birincil yapıcıdaki `val` ve `var` parametreleri özellik olur. Özelliklerin hesaplanan türü de vardır: `val area: Int get() = width * height` bir alanda saklanmaz, her okunduğunda hesaplanır. Parametrelere varsayılan değer verilebilir ve çağıran yer parametreleri adıyla, sırayı bozarak da verebilir. `var` bir özelliğe özel `set` yazılırsa atanan değer alana geçmeden önce işlenebilir; alanın kendisine `field` adıyla erişilir.

Aşağıdaki blok üç şeyi ölçer. `Rectangle(3)` yüksekliği varsayılan değer olan 1'e bırakır. `Rectangle(height = 2, width = 5)` parametreleri sırasını bozarak adıyla verir. Son olarak `label` özelliğine baş ve sonunda boşluk olan bir dizgi atanır; `set` bu boşlukları temizler.

```kotlin
// Main.kt — ozellikler
class Rectangle(val width: Int, val height: Int = 1) {
    val area: Int get() = width * height

    var label: String = "adsiz"
        set(value) {
            field = value.trim()
        }
}

fun main() {
    val narrow = Rectangle(3)
    println(narrow.area)
    val wide = Rectangle(height = 2, width = 5)
    println(wide.area)
    wide.label = "  kutu  "
    println("[" + wide.label + "]")
}
```

```
3
10
[kutu]
```

Alan 3 çarpı 1 ve 5 çarpı 2 olarak hesaplanmıştır. Etiket atandığı gibi değil, `set` içinden geçtikten sonra saklanmıştır: köşeli parantezler boşluk kalmadığını gösterir.

**TN12.** `area` bir alanda saklanmaz. Ölçülen blok yalnızca okuma sonucunu gösterir; `width` ve `height` değişebilir olsaydı `area`'nın değişeceği bu bloktan çıkmaz. Bu yalnızca yapıcıdan gelen `val` değerleri için ölçüldü.

## Equals Yazılmamış Bir Sınıf

Şimdi ölçüte dönülür. `Coordinate` sınıfının iki yapıcısı vardır: birincil yapıcı iki sayı alır, ikincil yapıcı `"3,4"` biçimindeki bir dizgiyi ayırarak aynı birincil yapıcıya yönlenir. Sınıfta `equals` yoktur. Kotlin'de her sınıf `Any`'den türer ve `Any`'nin `equals` yöntemi iki referansın aynı nesneyi gösterip göstermediğine bakar. Sınıf bunu yeniden tanımlamadıkça `==` işleci de kimliğe bakar.

```kotlin
// Coordinate.kt — equals yazilmamis sinif
class Coordinate(val x: Int, val y: Int) {
    constructor(text: String) : this(text.substringBefore(",").toInt(), text.substringAfter(",").toInt())
}
```

Ölçüm üç satırdan oluşur. İlkinde üç değerden ilk ikisi aynı nesnedir. İkincisinde iki ayrı `Coordinate(3, 4)` birincil yapıcıdan kurulur. Üçüncüsünde ikiz ikincil yapıcıdan, yani dizgiden kurulur; değerler yine 3 ve 4'tür.

```kotlin
// Main.kt — iki yoldan kurulan ornekler
fun main() {
    val same = Coordinate(1, 2)
    val checks = listOf(
        Criterion("ayni-nesne", same, same, Coordinate(1, 3)),
        Criterion("ayni-yol", Coordinate(3, 4), Coordinate(3, 4), Coordinate(3, 5)),
        Criterion("iki-yol", Coordinate(3, 4), Coordinate("3,4"), Coordinate(3, 5)),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
ayni-nesne: ayni-nesne=true, ikiz=true, farkli=false -> tekil
ayni-yol: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
iki-yol: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
tekil: 1
kimlik: 2
icerik: 0
kismi-icerik: 0
```

Kural ilk satırda ilk soruda durur: aynı nesne olduğu için ölçüt tekildir. İkinci ve üçüncü satırlarda alanlar birebir aynı olduğu hâlde `ikiz=false` çıkar ve ölçüt kimliktir. Kurulum yolu, yani birincil mi ikincil mi olduğu, sonucu değiştirmez. Aynı değerli iki örneği eşit yapan şey sınıfın alanları değildir; eşit yapan, `equals`'in ne yaptığıdır.

Bu dersin ölçüsünü bir ayrımla tamamlamak gerekir. Burada ölçülen, iki örneğin birbirine eşit sayılıp sayılmadığıdır. Bir örneğin başka bir yerden paylaşılıp paylaşılmadığı ya da bir değişikliğin başka bir referansta görünüp görünmediği ayrı bir sorudur ve bu derste ölçülmedi. Kimlik ölçütü yalnızca şunu söyler: `==` iki ayrı nesne için `false` verir.

**TN13.** Ölçüm yalnızca `Coordinate` ve iki yapıcı yolu içindir. Alanları başka tipte olan, ya da `Any`'den başka bir üst sınıftan türeyen bir sınıf için aynı sonuç ölçülmedi. Üst sınıf `equals`'i yeniden tanımlamışsa tablo başka olabilir.

## Kalıtımda Yapıcı Sırası

Bir sınıf başka bir sınıftan türediğinde yapıcı sırasına bir halka daha eklenir. Kotlin sınıfları varsayılan olarak kapalıdır; türetilecek sınıfa `open` yazılır ve alt sınıf, üst sınıfın yapıcısını başlık satırında çağırır. Aşağıdaki blokta hem üst sınıfın hem alt sınıfın bir `init` bloğu vardır. Hangisinin önce çalıştığı çıktıdan okunur.

```kotlin
// Main.kt — ust sinif yapicisi
open class Base(val name: String) {
    init {
        println("Base init: $name")
    }
}

class Derived(name: String, val extra: Int) : Base(name) {
    init {
        println("Derived init: $extra")
    }
}

fun main() {
    val item = Derived("ada", 3)
    println(item.name + item.extra)
}
```

```
Base init: ada
Derived init: 3
ada3
```

Çıktı sırayı açıkça gösterir: `Base init: ada` satırı `Derived init: 3` satırından önce gelir. Alt sınıfın başlık satırındaki `Base(name)` çağrısı, alt sınıfın kendi başlatıcıları çalışmadan önce üst sınıfı kurar. Nedeni basittir: alt sınıfın durumu üst sınıfın durumuna dayanabilir, bu yüzden dayanılan taraf önce hazır olmalıdır. Son satır, `name` değerinin üst sınıftan, `extra` değerinin alt sınıftan geldiğini ve ikisine tek nesne üzerinden erişildiğini gösterir.

Bu sıra, önceki bölümdeki çalışma sırasını genişletir. Tek sınıfta sıra özellik başlatıcıları, `init` blokları ve ikincil yapıcı gövdesidir; türetmede bunun önüne üst sınıfın tamamı eklenir. Üst sınıfın yapıcısı içinde alt sınıfın bir özelliğine dokunulursa o özellik henüz kurulmamıştır; bu nedenle üst sınıf yapıcıları alt sınıfa ait durumu okumaz. Sorumluluğun sınırı bu çıktıda görünür: her sınıf yalnızca kendi `init` bloğunda kendi alanlarını kurar.

## Okuma Başına Hesap ve Tek Seferlik Başlatıcı

Daha önce `area` özelliğinin bir alanda saklanmadığı, her okunduğunda hesaplandığı söylendi. Bunun ölçülebilir bir sonucu var: hesaplayan fonksiyon kaç kez çağrılır? Blokta iki özellik aynı fonksiyonu çağırır. Biri başlatıcıyla tanımlanmıştır, öteki `get()` ile. İkisi de üçer kez okunur; sayaçlar fonksiyonun kaç kez koştuğunu tutar.

```kotlin
// Main.kt — okuma basina hesap
var storedCalls = 0
var computedCalls = 0

fun compute(kind: String): Int {
    if (kind == "stored") storedCalls++ else computedCalls++
    return 10
}

class Holder {
    val stored: Int = compute("stored")
    val computed: Int get() = compute("computed")
}

fun main() {
    val holder = Holder()
    println(holder.stored + holder.stored + holder.stored)
    println(holder.computed + holder.computed + holder.computed)
    println("stored=$storedCalls computed=$computedCalls")
}
```

```
30
30
stored=1 computed=3
```

Çıktının son satırı sayaçları verir: `stored=1 computed=3`. İki özellik de üçer kez okundu ve ikisi de `30` toplamını verdi, ama hesaplayan fonksiyon farklı sayıda çalıştı. Başlatıcıyla tanımlanan özellik değeri nesne kurulurken bir kez hesaplar ve bir alanda saklar. `get()` ile tanımlanan özellik ise alan taşımaz; her okumada fonksiyon yeniden çalışır.

İki sonuç da aynı değeri döndürdüğü için fark çıktıdan değil, yalnızca sayaçtan görünür. Bu yüzden `get()` ile tanımlanan özellik, ucuz ve her seferinde güncel olması gereken değerler için uygundur: başka özelliklerden türeyen bir alan ya da bir toplam. Pahalı bir hesap ya da yan etkisi olan bir fonksiyon `get()` içine konursa her okuma o bedeli öder ve yan etki okuma sayısı kadar tekrarlanır. Hesabın bir kez yapılması gerekiyorsa başlatıcı, her seferinde güncel olması gerekiyorsa erişimci seçilir.

## Yazılan Equals ve Başka Tipler

Elle yazılan bir `equals` yalnızca aynı tipteki değerlerle karşılaşmaz. Parametresi `Any?` olduğu için `null` ve alakasız tipler de gelebilir. Yazdığımız `other is ValueCoordinate` denetimi bu durumları ayıklar. Blok `equals`'i dört kez çağırır: `null` ile, bir dizgiyle, aynı değerli başka bir örnekle ve nesnenin kendisiyle.

```kotlin
// Main.kt — equals ve baska tipler
class ValueCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean =
        other is ValueCoordinate && x == other.x && y == other.y

    override fun hashCode(): Int = 31 * x + y
}

fun main() {
    val point = ValueCoordinate(1, 2)
    val anything: Any = "1,2"
    println(point.equals(null))
    println(point.equals(anything))
    println(point == ValueCoordinate(1, 2))
    println(point.equals(point))
}
```

```
false
false
true
true
```

Dört çağrının hepsi `Boolean` döner ve çıktı `false`, `false`, `true`, `true` olur. `equals(null)` `false` verir, çünkü `is` denetimi `null` için başarısız olur. Dizgi `Any` olarak verildiğinde de `false` çıkar; tip uyuşmadığı için alanlara bakılmaz bile. Aynı değerli ayrı örnekte `true`, nesnenin kendisinde yine `true` döner.

Parametrenin `Any?` olması bir sözleşmedir: `equals` her türlü değerle karşılaşabilir ve istisna fırlatmadan `false` demelidir. Sık yapılan hata, parametreyi `ValueCoordinate` varsayıp doğrudan dönüştürmek, ya da tip denetimini atlayıp alanlara erişmektir. Doğru kalıp önce `is` denetimidir; `&&` zinciri sayesinde alan karşılaştırması yalnızca denetim geçtikten sonra yapılır ve derleyici sağ taraftaki `other` değerini `ValueCoordinate` olarak görür. `hashCode` aynı alanlardan hesaplanır; eşit nesneler eşit karma kodu üretmelidir, aksi halde karma tabanlı koleksiyonlar tutarsız çalışır.

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

Eşitliği sınıfın alanları değil, yazılan `equals` belirliyorsa aynı alanlara sahip sınıflar `equals`'e göre farklı ölçütlere düşmelidir. Sınırlayıcı ölçüm bunu üç satırla dener. `ValueCoordinate` iki alana da bakan bir `equals` ve onunla uyumlu bir `hashCode` yazar. `LeftCoordinate` yalnızca `x` alanına bakar. Üç satırdan ilki tam eşitliği, ikincisi `x` aynı `y` farklı durumu, üçüncüsü `x` farklı durumu dener. Bloğun sonundaki iki satır `hashSetOf` ile aynı değerli iki örneği bir kümeye koyar ve kümenin boyutunu basar; `equals` ile birlikte `hashCode`'un da yazıldığını bu yoldan gözlemler. `hashCode` değerlerinin kendisi basılmaz.

```kotlin
// Main.kt — equals yazilinca
class ValueCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean =
        other is ValueCoordinate && x == other.x && y == other.y

    override fun hashCode(): Int = 31 * x + y
}

class LeftCoordinate(val x: Int, val y: Int) {
    override fun equals(other: Any?): Boolean = other is LeftCoordinate && x == other.x

    override fun hashCode(): Int = x
}

fun main() {
    val checks = listOf(
        Criterion("tam", ValueCoordinate(3, 4), ValueCoordinate(3, 4), ValueCoordinate(3, 5)),
        Criterion("yalniz-x-y-farkli", LeftCoordinate(3, 4), LeftCoordinate(3, 4), LeftCoordinate(3, 5)),
        Criterion("yalniz-x-x-farkli", LeftCoordinate(3, 4), LeftCoordinate(3, 4), LeftCoordinate(2, 4)),
    )
    for (check in checks) check.report()
    summarize(checks)
    println(hashSetOf(Coordinate(3, 4), Coordinate(3, 4)).size)
    println(hashSetOf(ValueCoordinate(3, 4), ValueCoordinate(3, 4)).size)
}
```

```
tam: ayni-nesne=false, ikiz=true, farkli=false -> icerik
yalniz-x-y-farkli: ayni-nesne=false, ikiz=true, farkli=true -> kismi-icerik
yalniz-x-x-farkli: ayni-nesne=false, ikiz=true, farkli=false -> icerik
tekil: 0
kimlik: 0
icerik: 2
kismi-icerik: 1
2
1
```

Aynı sınıf kalıbı üç satırda iki ölçüt verir. `ValueCoordinate` iki alanı da karşılaştırdığı için ikiz ve farklı değer ayrışır: içerik. `LeftCoordinate` `y` farklı olduğunda bile eşit çıkar, dolayısıyla üçüncü değer ikizle aynı sonucu verir ve ölçüt kısmi içeriğe düşer; `x` farklı olduğunda ise içerik çıkar. Aynı sınıfın iki ayrı satırı iki ayrı ölçüt verir; demek ki ölçütü bir sınıf değil, `equals`'in baktığı alanlar ve karşılaştırılan değer çifti birlikte belirler. Kümelerde `Coordinate` iki ayrı öğe olarak kalır, `ValueCoordinate` tek öğeye iner.

**TN14.** Bu ölçüm iki alanlı küçük bir sınıf içindir. Daha çok alanı olan bir sınıfta hangi alanların `equals`'e girdiği her sınıfa özgüdür; aynı davranış ölçülmeden varsayılamaz.

## Özet

- Birincil yapıcı sınıf başlığına yazılır; başlatıcılar ve `init` blokları yazılış sırasıyla koşar, ikincil yapıcının gövdesi en sonda.
- İkincil yapıcı `: this(...)` ile birincil yapıcıya yönlenmek zorundadır ve yönlenilen yapıcı önce tamamlanır.
- Özelliklere varsayılan değer, adlı argüman, hesaplanan okuma ve özel `set` ile davranış verilebilir.
- `equals` yazılmamış bir sınıfın aynı değerli örnekleri, hangi yapıcıyla kurulurlarsa kurulsunlar, kimlik ölçütüne düşer.
- Bu dersin ölçütü: **kimlik**. Sınırlayıcı ölçümde aynı sınıf kalıbı `equals`'e göre içeriğe ve kısmi içeriğe de düştü.

## Sonraki Adım

Bir sınıfı değer gibi davranacak hâle getirmek için `equals`, `hashCode` ve `toString`'i elle yazmak gerekti. Bir sonraki ders bu üçünü birincil yapıcıdaki özelliklerden üreten veri sınıflarını ölçer: `copy` ile kurulan ayrı nesnenin eşit sayılıp sayılmadığına ve yapısöküm bildiriminin ne yaptığına bakar.
