---
title: 'Arayüzler ve Devretme'
source: 'https://academia.sh/tr/kurslar/kotlin/arayuzler-ve-devretme'
course: 'Kotlin Dili'
language: tr
updated: '2026-10-21T05:02:40+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir arayüzde özellik ve varsayılan gerçekleştirimli fonksiyon yazmak'
    - 'İki arayüzün aynı varsayılan fonksiyonunu tek sınıfta nasıl birleştireceğini göstermek'
    - '`by` ile bir arayüzün gerçekleştirimini başka bir nesneye devretmek'
    - 'Devretmenin eşitliği iletmediğini ve veri sınıfıyla birlikte ne olduğunu ölçmek'
---

# Arayüzler ve Devretme

Arayüzü, varsayılan gerçekleştirimi ve `by` ile devretmeyi iki değeri eşit yapan ölçütle ölçer; devretmenin arayüzün üyelerini ilettiğini, `equals`'ü iletmediğini gösterir.

Önceki derste bir nesnenin tekil mi yoksa her seferinde yeni mi kurulduğuna baktık. Bu derste nesnenin davranışını sınıfın dışına çıkarıyoruz: arayüz bir sözleşmeyi yazar, bir sınıf o sözleşmeyi ya kendisi gerçekleştirir ya da başka bir nesneye devreder. Soru devretmeye geliyor: bir nesne davranışını başkasına bıraktığında, iki devreden nesneyi eşit yapan ne?

**Arayüz (interface)**, bir tipin uyması gereken üyeleri tanımlar. Kotlin'de bir arayüz yalnız imza değil, gövdeli fonksiyon ve gövdeli okunur özellik de taşıyabilir; bunlara varsayılan gerçekleştirim denir. Bir sınıf arayüzü gerçekleştirdiğinde gövdesiz üyeleri yazmak zorundadır, gövdelileri isterse yeniden tanımlar (override).

Bu derste ölçüt eksenini bir kez daha hatırlatalım. Java müfredatındaki, kararı hangi tipin verdiğini soran ölçü (M08/K02) burada yok: biz tipe değil, iki değerin eşit sayılmasının nedenine bakıyoruz. Aynı nesne olmaları tekil, eşitliğin kimliğe düşmesi kimlik, içeriklerin eşit olması içerik, yalnız bir kısmının eşit olması kısmi içerik.

## Ölçüm Düzeneği

`Criterion` her derste ilk blokta aynı biçimde durur. Her gözlem üç değer taşır: `first`, `twin` (aynı tarifle ayrıca kurulan değer) ve `other` (yalnız bir bakımdan farklı değer). Kural sonucu görür, gerekçeyi görmez.

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

Dersin kendi modeli ikinci bir yardımcı dosyada durur. `Printer` arayüzü bir okunur özellik ve bir varsayılan fonksiyon taşır; iki sınıf onu farklı biçimde gerçekleştirir.

```kotlin
// Printers.kt — arayuzde ozellik ve varsayilan gerceklestirim
interface Printer {
    val prefix: String
    val width: Int
        get() = prefix.length

    fun render(text: String): String = prefix + text
}

class PlainPrinter : Printer {
    override val prefix: String = "> "
}

class LoudPrinter : Printer {
    override val prefix: String = "# "
    override fun render(text: String): String = prefix + text.uppercase()
}
```

**TN26.** `Printer`'ın yalnız bir gövdesiz özelliği ve bir gövdeli okunur özelliği var; arayüzde saklanan (alan taşıyan) bir özellik yazılamaz ve bu burada ölçülmedi. Aşağıdaki bloklar iki `Printer` gerçekleştirimini ve `by` ile yazılan sarmalayıcıları bu yardımcıyla birlikte derler.

## Arayüz Üyesi Gövde Taşıyabilir

Aşağıdaki blok iki sınıfı yan yana kullanır. `PlainPrinter` yalnız gövdesiz özelliği yazdı; `render` ile `width` arayüzdeki gövdeleri aldı. `LoudPrinter` ise `render`'ı yeniden tanımladı. Son satır iki `PlainPrinter`'ı ölçer.

```kotlin
// Main.kt — arayuz varsayilani ve yeniden tanimlama
fun main() {
    val plain = PlainPrinter()
    val loud = LoudPrinter()
    println(plain.render("ada"))
    println(loud.render("ada"))
    println(plain.width)
    val printers: List<Printer> = listOf(plain, loud)
    println(printers.map { it.render("x") })
    Criterion("iki-varsayilan", PlainPrinter(), PlainPrinter(), loud).report()
}
```

```
> ada
# ADA
2
[> x, # X]
iki-varsayilan: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
```

`plain` arayüzün `render` gövdesini kullandı, `loud` kendi gövdesini; `printers` listesinden ikisi de aynı `Printer` adıyla çağrıldı ve her biri kendi sonucunu verdi. `width` yalnız arayüzde tanımlıydı ve `prefix`'in uzunluğunu verdi. Son satırda iki ayrı `PlainPrinter` `kimlik` çıktı: sınıf `equals`'ü yeniden tanımlamadığı için eşitlik kimliğe düştü ve iki örnek aynı arayüz gövdesini kullansa da eşit olmadı. Varsayılan gerçekleştirim davranışı paylaştırır, eşitliği paylaştırmaz.

## Çakışan Varsayılanlar

Bir sınıf iki arayüzü gerçekleştirir ve ikisi de aynı imzalı gövdeli fonksiyon taşırsa, derleyici hangisinin seçileceğini bilemez. Sınıfın fonksiyonu kendisinin yeniden tanımlaması gerekir ve gövde içinde `super<Arayüz>` ile her birine ayrı ayrı ulaşılabilir.

```kotlin
// Main.kt — iki arayuzun ayni varsayilani tek sinifta birlesir
interface Reader {
    fun describe(): String = "okur"
}

interface Writer {
    fun describe(): String = "yazar"
}

class Both : Reader, Writer {
    override fun describe(): String = super<Reader>.describe() + "+" + super<Writer>.describe()
}

fun main() {
    println(Both().describe())
    println(Both().let { it as Reader }.describe())
}
```

```
okur+yazar
okur+yazar
```

`Both` iki kaynağı birleştirdi. İkinci satır önemli: `Both` nesnesi `Reader` olarak görüldüğünde de aynı birleşik gövde koştu, çünkü seçimi nesnenin kendi yeniden tanımladığı fonksiyon yaptı, değişkenin tipi değil.

**TN27.** Bu blokta çakışmayı çözmeyen bir sınıfın derlenmediğini ayrıca doğrulamadık; yalnız çözülmüş hâli ölçüldü.

## by ile Devretme

**Devretme (delegation)**, bir arayüzün gerçekleştirimini başka bir nesneye bırakmaktır. Kotlin'de bu `: Printer by inner` biçiminde yazılır: derleyici arayüzün her üyesi için, çağrıyı `inner` üzerine ileten bir fonksiyon üretir. İleten nesneye devreden, çağrının gittiği nesneye **temsilci (delegate)** denir.

```kotlin
// Main.kt — by ile devretme arayuzun uyelerini iletir
class Labeled(private val inner: Printer) : Printer by inner

class Quiet(private val inner: Printer) : Printer by inner {
    override fun render(text: String): String = inner.render(text).lowercase()
}

fun main() {
    val loud = LoudPrinter()
    val labeled = Labeled(loud)
    println(labeled.render("ada"))
    println(labeled.prefix)
    println(labeled.width)
    println(Quiet(loud).render("ADA"))
    val checks = listOf(
        Criterion("ayni-temsilci", Labeled(loud), Labeled(loud), Labeled(PlainPrinter())),
        Criterion("yeniden-tanimli", Quiet(loud), Quiet(loud), Quiet(PlainPrinter())),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
# ADA
#
2
# ada
ayni-temsilci: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
yeniden-tanimli: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
tekil: 0
kimlik: 2
icerik: 0
kismi-icerik: 0
```

`Labeled` hiçbir üye yazmadı; `render`, `prefix`, `width` yine de çalıştı: üçü de `loud`'a gitti. `render` sonucunun büyük harfli olması, çağrının `Printer`'ın varsayılan gövdesine değil `LoudPrinter`'ın yeniden tanımladığı gövdeye gittiğini gösteriyor. `Quiet` ise bir üyeyi kendisi yeniden tanımladı ve geri kalanını devretti.

Aynı temsilciyi saran iki `Labeled` ve aynı temsilciyi saran iki `Quiet` ikisi de `kimlik` çıktı. İki devreden nesne aynı `loud`'a bağlı olsa da eşit sayılmadı.

**TN28.** `by`'dan sonra gelen ifadenin bir kurucu parametresi olması şart değildir; burada yalnız parametreyi gösteren biçimi ölçtük. Devretmenin, arayüzde olmayan bir üyeyi de iletip iletmediği ölçülmedi.

## Fonksiyonel Arayüz, Nesne İfadesi ve Devir İfadesi

Arayüz yalnız sınıflarla gerçekleştirilmez. Tek bir soyut fonksiyonu olan bir arayüz, `fun interface` ile işaretlenirse bir lambda ile kurulabilir. Bir arayüz ayrıca adsız bir nesne ifadesiyle de gerçekleştirilebilir. Üçüncü olarak, `by`'dan sonra gelen ifade bir kurucu parametresi olmak zorunda değildir; herhangi bir ifade olabilir. Bu üçünü tek bir blokta ölçüyoruz.

`Shout` iki lambda ile kurulur: ikisinin gövdesi aynıdır. `anon` bir nesne ifadesiyle `Printer` gerçekleştirir. `Cached` sınıfı ise temsilciyi `makePrinter()` çağrısıyla kurar ve `made` sayacı, bu çağrının kaç kez koştuğunu sayar.

```kotlin
// Main.kt — fun interface, nesne ifadesi ve devir ifadesinin bir kez degerlendirilmesi
fun interface Shout {
    fun apply(text: String): String
}

var made = 0

fun makePrinter(): Printer {
    made += 1
    return PlainPrinter()
}

class Cached : Printer by makePrinter()

fun main() {
    val first = Shout { it.uppercase() }
    val second = Shout { it.uppercase() }
    println(first.apply("ada"))
    val anon = object : Printer {
        override val prefix: String = "~ "
    }
    println(anon.render("x"))
    val one = Cached()
    println(one.render("a"))
    println(one.render("b"))
    println(made)
    Cached()
    println(made)
    val checks = listOf(
        Criterion("fonksiyonel-arayuz", first, second, Shout { it }),
        Criterion("nesne-ifadesi", anon, anon, PlainPrinter()),
    )
    for (check in checks) check.report()
}
```

```
ADA
~ x
> a
> b
1
2
fonksiyonel-arayuz: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
nesne-ifadesi: ayni-nesne=true, ikiz=true, farkli=false -> tekil
```

Bloğun ilk sonucu, gövdesi aynı olan iki lambdanın eşit sayılmadığıdır: `fonksiyonel-arayuz` satırı `kimlik` çıktı. Gövdelerin metin olarak aynı olması eşitlik üretmedi; her lambda ifadesi kendi nesnesini kurdu ve fonksiyonel arayüz `equals`'ü yeniden tanımlamıyor. Aynı arayüzü sınıfla gerçekleştirmek ile lambda ile gerçekleştirmek eşitlik bakımından ayrılmaz.

İkinci sonuç nesne ifadesi içindir. `nesne-ifadesi` satırı `tekil` çıktı, çünkü `anon` tek bir değişkendir ve aynı değer iki kez ölçüye verildi. Önceki derste gördüğümüz ayrım burada da geçerli: ifade her değerlendirildiğinde yeni bir nesne kurar, ama bir kez kurulup bir değişkene konan nesne tek kalır. `anon.render("x")` çıktısındaki `~ ` öneki, arayüzdeki varsayılan `render` gövdesinin nesne ifadesinde de kullanıldığını gösterdi.

Üçüncü sonuç `by` ifadesi içindir. `made` ilk `Cached()` çağrısından sonra `1`, ikinciden sonra `2` yazdı. Temsilci ifadesi her `Cached` örneği kurulurken bir kez değerlendirilir: iki örnek, iki ayrı `PlainPrinter` taşır. Aynı örneğin iki `render` çağrısı ise temsilciyi yeniden kurmadı; `made` yalnız kurucuda arttı. Bu, devretmenin bir yöntem çağrısı değil, kurucuda saklanan bir alan olduğunu söyler: temsilci örnek başına bir kez kurulur ve sonra iletilir.

**TN29.** Bir lambdanın dış değişkeni yakalayıp yakalamamasının eşitliği değiştirip değiştirmediği ölçülmedi; iki lambda da hiçbir şey yakalamadı.



## Ölçümlerden Çıkan Eşitlik Kuralı

Bu derste ölçtüğümüz her satır, bir davranışın nereden geldiğini sorduktan sonra eşitliğin nereden geleceğini sordu. Arayüz davranış paylaşır ama durum taşımaz ve `equals` yazmaz; `by` davranışı bir temsilciye iletir ama `equals`'ü iletmez; `fun interface` bir lambdayı arayüz değerine çevirir ama iki özdeş lambdayı eşit saymaz. Üç düzeneğin ortak sonucu, eşitliğin davranışın paylaşılmasıyla değil, sınıfın kendi `equals` tanımıyla belirlendiğidir.

Sık yapılan hata, devretmeyi kalıtım gibi okumaktır. Kalıtımda alt sınıf üst sınıfın `equals`'ünü miras alır; `by` ile devredilen bir sınıf ise arayüzün üyelerini iletir, ama `equals` arayüzde tanımlı bir üye olmadığı için iletilmez. Bu yüzden aynı temsilciyi saran iki `Labeled` ya da iki `Quiet` örneği, davranışları birebir aynı olsa da eşit çıkmadı. Eşitlik gerekiyorsa sınıf bunu kendisi yazar ya da `data class` olarak bildirir; devretme bu işi yapmaz.

Devir ifadesinin örnek başına bir kez değerlendirilmesi ikinci bir pratik sonuç verir. `made` sayacı iki örnek için `2` yazdı; yani temsilci bir alandır, örneğin ömrü boyunca aynı kalır. Bir temsilcinin içinde durum tutuluyorsa, örneğin bir sayaç ya da bir önbellek, o durum devreden örneğe özgüdür ve iki örnek arasında paylaşılmaz.

Lambda ile kurulan arayüz değerlerinde de aynı ders geçerlidir: iki gövde aynı metni taşısa bile iki ayrı değerdir ve sonuç `kimlik` çıkar. Eşitliği davranıştan beklemek, bu derste ölçtüğümüz her düzenekte yanlış çıktı. Davranış ile eşitliği ayrı düşünmek, bir koleksiyonda ya da bir `==` karşılaştırmasında beklenmedik bir sonuçla karşılaşmamayı sağlar.

## Ölçmeden Önce Tahmin Etmek

Bu dersin ölçütü `kimlik` olduğu için tahmin kolaydır, ama hangi satırın neden `kimlik` verdiğini söylemek daha öğreticidir. Sınıf ile gerçekleştirilen bir arayüzde eşitlik sınıfın `equals`'üne bağlıdır; sınıf onu yazmadıysa `Object`'in kimlik karşılaştırması geçerlidir. `by` bu tabloya bir şey eklemez, çünkü iletilen üyeler arayüzde tanımlı olanlardır. Veri sınıfı olarak bildirilen bir sınıfta `equals` derleyici tarafından üretilir ve içeriğe bakar; bu ders onu bir karşılaştırma olarak gösterdi.

Fonksiyonel arayüz satırında tahmin tuzağı vardır. İki lambda aynı metni taşıyor ve aynı arayüze çevriliyor; "aynı iş yapıyorlar, demek ki eşitler" diye düşünmek doğal görünür. Ölçüm `kimlik` verdi. Nesne ifadesi satırında ise aynı değişken iki kez verildi ve sonuç `tekil` oldu; ifade değerlendirilirken yeni nesne kurulur, ama bir değişkene konan nesne bundan sonra tek kalır. Bu iki satırın farkı, "iki ifade" ile "bir ifade, iki okuma" arasındaki farktır.

Devir ifadesinde tahmin edilecek şey bir sayıdır. İki `Cached` kuran bir program için `made` değeri `2` olacaktır, çünkü temsilci kurucuda bir kez değerlendirilir. Bu sayıyı `1` ya da çağrı sayısı kadar bekleyen kişi, devretmenin bir alan mı, yoksa her çağrıda yeniden hesaplanan bir ifade mi olduğunu yanlış anlamıştır.

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

Neden iki devreden nesne eşit değil? Çünkü `by` yalnız arayüzün üyelerini iletir; `equals`, `hashCode` ve `toString` arayüzün üyesi değil, `Any`'nin üyesidir. Önce bunu doğruladık: aşağıdaki parça Kotlin 2.4.21'de derlenmedi, bu yüzden çıktısız kalıyor. Bir arayüzün `Any`'nin `equals`'ü için varsayılan gerçekleştirim vermesi de yasak.

```kotlin
interface Same {
    override fun equals(other: Any?): Boolean = true
}
```

Devretme `equals`'ü iletmediğine göre, eşitliği değiştiren tek yol sınıfın kendisinin onu yeniden tanımlamasıdır. Veri sınıfı bunu otomatik yapar. Aşağıdaki blok, aynı temsilciyi saran bir sıradan sınıfı ve bir veri sınıfını yan yana ölçer; üçüncü satır devreden nesne ile temsilcisini karşılaştırır.

```kotlin
// Main.kt — devretme equals'u iletmez, veri sinifi kendi uretir
class Wrapped(val inner: Printer) : Printer by inner

data class Tagged(val inner: Printer) : Printer by inner

fun main() {
    val loud = LoudPrinter()
    val checks = listOf(
        Criterion("sinif-devretme", Wrapped(loud), Wrapped(loud), Wrapped(PlainPrinter())),
        Criterion("veri-sinifi-devretme", Tagged(loud), Tagged(loud), Tagged(PlainPrinter())),
        Criterion("devreden-ve-temsilci", Wrapped(loud), loud, PlainPrinter()),
    )
    for (check in checks) check.report()
    summarize(checks)
}
```

```
sinif-devretme: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
veri-sinifi-devretme: ayni-nesne=false, ikiz=true, farkli=false -> icerik
devreden-ve-temsilci: ayni-nesne=false, ikiz=false, farkli=false -> kimlik
tekil: 0
kimlik: 2
icerik: 1
kismi-icerik: 0
```

Sıradan sınıfta ölçüt `kimlik`, veri sınıfında `icerik` çıktı. Veri sınıfı kendi `equals`'ünü üretip birincil yapıcıdaki `inner`'ı karşılaştırdığı için, aynı temsilciyi saran iki örnek eşit oldu; farklı temsilciyi saran ise eşit olmadı. Üçüncü satır devretmenin sınırını açık gösteriyor: `Wrapped(loud)` ile `loud` birbirine eşit değil. Devreden nesne temsilcisi gibi davranır ama temsilcisi değildir.

## Özet

- Arayüz, bir tipin uyması gereken üyeleri yazar; gövdeli fonksiyon ve gövdeli okunur özellik taşıyabilir.
- İki arayüz aynı varsayılan fonksiyonu getirirse sınıf onu yeniden tanımlar ve `super<Arayüz>` ile ikisine ayrı ulaşır.
- `by`, arayüzün üyelerini temsilciye iletir; temsilcinin kendi yeniden tanımladığı gövdeler korunur.
- Devretme `equals`'ü iletmez ve bir arayüz `equals` için varsayılan gerçekleştirim veremez; aynı temsilciyi saran iki sıradan nesne eşit değildir, iki veri sınıfı ise eşittir.
- Bu dersin ölçütü: **kimlik**. Davranış paylaşılsa bile eşitlik, sınıfın `equals`'ünü yeniden tanımlamadıkça kimliğe düşer.

## Sonraki Adım

Devretme, davranışı sınıfın içinde başka bir nesneye taşıdı. Sıradaki ders davranışı sınıfın dışından ekliyor: uzantı fonksiyonları, kaynağına dokunmadığınız bir tipe yeni fonksiyon yazdırır. Bir uzantının, tipin kendi üyesinin yerine geçip geçemeyeceğini, özellikle `equals` üzerinde ölçeceğiz.
