---
title: Genellikler
source: 'https://academia.sh/tr/kurslar/swift/genellikler'
course: 'Swift Dili'
language: tr
updated: '2026-10-21T05:03:12+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Tip parametresi ve kısıtla genel bir fonksiyon ve genel bir yapı yazmak'
    - 'İlişkili tipli bir protokolü `where` kısıtıyla bir fonksiyonda kullanmak'
    - "Tembel `map` ile istekli `map`'in dönüştürücüyü ne zaman çağırdığını ölçmek"
    - 'Tembel bir dizinin okunmayan elemanının hiç değerlendirilmediğini göstermek'
---

# Genellikler

Tip parametreleri ve kısıtlarla tek bir gövdeyi birçok tipe uygular; tembel dizinin dönüştürücüsünü her erişimde, istekli map'inkini hemen ölçer.

Önceki derste `Int` için yazdığımız `cubed`, `Double` için yeniden yazılmak zorundaydı. Aynı gövdeyi bir kez yazıp birçok tipe uygulamak, genelliklerin işidir. **Genellik (generics)**, bir fonksiyonu ya da tipi, henüz belli olmayan bir tip için yazmayı sağlar; tip, kullanım anında belli olur. Bu derste iki davranışı ölçüyoruz: genel bir gövdenin her çağrıda nasıl koştuğunu ve standart kitaplığın tembel dizilerinin dönüştürücüyü ne zaman çağırdığını.

**Tip parametresi (type parameter)**, açılı parantez içinde yazılan yer tutucudur: `<T>`. **Kısıt (constraint)**, o yer tutucunun taşıması gereken bir protokolü söyler: `<T: Comparable>` yalnız karşılaştırılabilen tipleri kabul eder. Kısıt olmadan gövde `T` hakkında hiçbir şey bilmez; `<` işlecini kullanamaz bile.

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

`Probe` her derste aynı biçimde ilk bloktur. Bu derste ayrıca genel bir yapı ve ilişkili tipli bir protokol, ayrı bir yardımcı dosyada durur.

```swift
// Probe.swift — bir ifadenin ne zaman degerlendirildigini kaydeder
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "hic" }
        if evaluations > 1 { return "her-kullanimda" }
        return evaluatedBeforeUse ? "hemen" : "ilk-kullanimda"
    }

    func report() {
        print("\(label): \(evaluations) degerlendirme, \(uses) kullanim -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["hemen", "ilk-kullanimda", "her-kullanimda", "hic"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

**TS35.** Her gözlem, `hic` dışında, iki kullanım taşır; dönüştürücüyü tek elemanlı dizilere bağlayarak eleman sayısının değerlendirme sayısını şişirmesini önlüyoruz.

```swift
// Stack.swift — genel yigit ve iliskili tipli protokol
protocol Container {
    associatedtype Item
    var count: Int { get }
    mutating func append(_ item: Item)
}

struct Stack<Element>: Container {
    private(set) var items: [Element] = []
    var count: Int { items.count }
    mutating func append(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
}
```

`Container`, bir **ilişkili tip (associated type)** taşır: `Item`. Protokol hangi tipin saklanacağını söylemez, yalnız böyle bir tipin bulunacağını söyler; uyan tip belirler. `Stack<Element>` ise bir **genel yapı**dır: `Element` yer tutucusu, `Stack<Int>` ya da `Stack<String>` yazıldığında gerçek bir tipe dönüşür ve `Container`'ın `Item` tipi `Element` olarak çıkarılır.

## Genel Gövde Her Çağrıda Koşar

Aşağıdaki blok üç şeyi yan yana koyar. `largest(_:tracking:)`, `Comparable` kısıtlı genel bir fonksiyondur ve iki farklı tipte çağrılır. `describe`, ilişkili tipe `where` ile kısıt koyar. Son iki gözlem aynı dönüşümün iki biçimidir: istekli `map` ve `lazy.map`.

```swift
// main.swift — genel fonksiyon, genel yapi, istekli ve tembel map
func largest<T: Comparable>(_ values: [T], tracking tracker: Probe) -> T? {
    tracker.eval(values.max())
}

func describe<C: Container>(_ box: C) -> Int where C.Item: Comparable {
    box.count
}

let generic = Probe("genel-fonksiyon")
let eager = Probe("istekli-map")
let lazyMap = Probe("lazy-map")

_ = generic.use { largest([3, 9, 4], tracking: generic) }
_ = generic.use { largest(["b", "a"], tracking: generic) }

var stack = Stack<Int>()
stack.append(5)
stack.append(8)
print(describe(stack))
print(stack.pop() ?? -1)

let eagerDoubled = [4].map { eager.eval($0 * 2) }
_ = eager.use { eagerDoubled[0] }
_ = eager.use { eagerDoubled[0] }

let lazyDoubled = [4].lazy.map { lazyMap.eval($0 * 2) }
_ = lazyMap.use { lazyDoubled[0] }
_ = lazyMap.use { lazyDoubled[0] }

let all = [generic, eager, lazyMap]
for probe in all { probe.report() }
tally(all)
```

```
2
8
genel-fonksiyon: 2 degerlendirme, 2 kullanim -> her-kullanimda
istekli-map: 1 degerlendirme, 2 kullanim -> hemen
lazy-map: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 1
ilk-kullanimda: 0
her-kullanimda: 2
hic: 0
```

Genel fonksiyon her çağrıda koştu: `largest` iki çağrıda iki kez değerlendirildi, ve çağrılar farklı tiplerle yapıldı. Genel bir gövde tek bir yazılıştır ama her tip için ayrı bir çağrıdır; değerlendirme anı tip parametresine bağlı değildir. `describe` ve `Stack` yalnız tip düzeyinde çalıştı: `where C.Item: Comparable` kısıtı, `Stack<Int>`'in `Item`'ı `Int` olduğu için sağlandı, ve derleyici bunu çağrı anında denetledi.

Asıl ayrım son iki satırda. İstekli `map`, dizinin elemanlarını hemen dönüştürüp yeni bir dizi kurar; dönüştürücü `eagerDoubled` kurulurken bir kez koştu ve iki okuma yeniden hesaplamadı, `hemen`. `lazy.map` ise dönüştürücüyü saklayan bir **tembel sarmalayıcı** döndürür; dizi hiçbir şey hesaplamadan kurulur ve her erişimde dönüştürücü yeniden çağrılır. Aynı elemana iki erişim iki değerlendirme verdi: `her-kullanimda`. **TS36.** Bu sonuç dizi tabanlı tembel koleksiyon için ölçüldü; tembel bir `Sequence`'in yeniden dolaşılabilir olduğu varsayılmadı.

Tembel `map` hesaplanan özellik gibi davranır, saklanan özellik gibi değil: sonuç hiçbir yerde durmaz. Hesabı bir kez yapıp saklamak istersen dizi sarmalayıcıdan bir dizi türetirsin, ki bu istekli bir adımdır.

## `where` Kısıtı İki Tipi Bağlar

Tek bir tip parametresine koyulan kısıt (`T: Comparable`) bir protokolü zorunlu kılar. **`where` yan tümcesi** ise daha fazlasını söyleyebilir: iki tip parametresinin ilişkili tiplerinin aynı olmasını ya da bir ilişkili tipin bir protokole uymasını isteyebilir. Aşağıdaki fonksiyon iki kapsayıcıyı alır ve yalnız eleman tipleri aynıysa karşılaştırır: `A.Item == B.Item`. Bu bir derleme zamanı denetimidir; ölçümde gövdenin değerlendirme anının değişmediğini görüyoruz.

```swift
// main.swift — where ile iki kapsayicinin eleman tipini baglamak
func sameCount<A: Container, B: Container>(_ left: A, _ right: B, tracking tracker: Probe) -> Bool
    where A.Item == B.Item {
    tracker.eval(left.count == right.count)
}

let whereProbe = Probe("where-kisiti")

var first = Stack<Int>()
first.append(1)
var second = Stack<Int>()
second.append(2)
second.append(3)

_ = whereProbe.use { sameCount(first, second, tracking: whereProbe) }
_ = whereProbe.use { sameCount(first, first, tracking: whereProbe) }

whereProbe.report()
```

```
where-kisiti: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

Gövde iki çağrıda iki kez koştu: `her-kullanimda`. `where` yan tümcesi, çağrının geçerli olup olmadığını belirler; geçerli bir çağrının maliyetine ya da zamanına karışmaz. İki yığıtın eleman tipinin aynı olması burada derleyicinin çıkarımıdır ve ölçümde iz bırakmaz. Farklı eleman tipli iki yığıtın bu fonksiyona verilemeyeceğini ayrıca denemedik; kısıtın ne yaptığını gövdenin davranışından değil, imzanın okunuşundan anlıyoruz. **TS37.** Kısıtın reddedilmesi bu derste ölçülmedi.

## Genel Yapıya Kısıtlı Uzantı

07. derste `Array`'e kısıtlı uzantı eklemiştik. Aynı şey kendi genel tipimiz için de geçerlidir: `Stack` her eleman tipiyle kullanılabilir, ama karşılaştırma isteyen bir yöntem yalnız `Element: Comparable` olan yığıtlarda anlamlıdır. Kısıtlı uzantı, yöntemi o durumla sınırlar. Aşağıdaki uzantı en büyük elemanı bulur.

```swift
// main.swift — Stack icin kisitli uzanti
extension Stack where Element: Comparable {
    func largest(tracking tracker: Probe) -> Element? {
        tracker.eval(items.max())
    }
}

let constrained = Probe("kisitli-uzanti")

var numbers = Stack<Int>()
numbers.append(4)
numbers.append(9)

_ = constrained.use { numbers.largest(tracking: constrained) }
_ = constrained.use { numbers.largest(tracking: constrained) }

constrained.report()
```

```
kisitli-uzanti: 2 degerlendirme, 2 kullanim -> her-kullanimda
```

`largest` her çağrıda yığıtı yeniden tarar: `her-kullanimda`. Yığıtın içinde bir önbellek yok; uzantı, 07. dersteki kuralın aynısına tabidir ve saklanan bir alan ekleyemez. Genel tiplerin avantajı burada şudur: `Stack<Int>` için `largest` var, `Stack<Counter>` gibi karşılaştırılamayan bir eleman tipi için ise kısıtlı uzantının yöntemi sunulmaz. Genel yapı bir kez yazılır, yöntem kümesi ise eleman tipine göre değişir. **TS38.** Ölçüm yalnız `Int` elemanlı yığıtda yapıldı; `Comparable` olmayan elemanla yöntemin görünmediği ayrıca doğrulanmadı.

## `some` ile Opak Dönüş Tipi

Bir genel fonksiyon, dönüş tipini çağıranın seçmesine bırakmak yerine kendisi seçebilir ve bunu gizleyebilir. **Opak dönüş tipi (opaque return type)**, `some Sequence<Int>` gibi yazılır: fonksiyon belirli bir tip döndürür, ama imza yalnız "bu protokole uyan bir şey" der. Çağıran gerçek tipi bilmez, derleyici bilir. Burada soru, opak dönüşün değerlendirme anını değiştirip değiştirmediğidir. İki fonksiyon aynı dönüşümü yapar; biri `lazy` dizi, öteki istekli dizi döndürür, ikisi de `some Sequence<Int>` olarak.

```swift
// main.swift — some ile opak donus: tembel ve istekli
func lazySquares(of values: [Int], tracking tracker: Probe) -> some Sequence<Int> {
    values.lazy.map { tracker.eval($0 * $0) }
}

func eagerSquares(of values: [Int], tracking tracker: Probe) -> some Sequence<Int> {
    values.map { tracker.eval($0 * $0) }
}

let lazyOpaque = Probe("opak-tembel")
let eagerOpaque = Probe("opak-istekli")

let lazyResult = lazySquares(of: [3], tracking: lazyOpaque)
let eagerResult = eagerSquares(of: [3], tracking: eagerOpaque)

_ = lazyOpaque.use { Array(lazyResult) }
_ = lazyOpaque.use { Array(lazyResult) }
_ = eagerOpaque.use { Array(eagerResult) }
_ = eagerOpaque.use { Array(eagerResult) }

let all = [lazyOpaque, eagerOpaque]
for probe in all { probe.report() }
```

```
opak-tembel: 2 degerlendirme, 2 kullanim -> her-kullanimda
opak-istekli: 1 degerlendirme, 2 kullanim -> hemen
```

İmzaları aynı olan iki fonksiyon, farklı anlar verdi. Tembel olan, her tüketimde yeniden hesapladı: `her-kullanimda`. İstekli olan, çağrı sırasında bir kez hesapladı ve iki tüketim bunu tekrar etmedi: `hemen`. `some`, gerçek tipi gizler ama davranışı gizlemez; dönen şeyin tembel mi istekli mi olduğu imzada yazmaz, ölçümde görünür. Bu yüzden opak dönüş tipi taşıyan bir API'de tüketimin maliyetini belgede ayrıca söylemek yararlıdır. **TS39.** İki fonksiyon tek elemanlı girdiyle ölçüldü; çok elemanlı girdideki değerlendirme sayısı burada kasıtlı olarak dışarıda bırakıldı.

## `@autoclosure` Parametreli Genel Fonksiyon

Standart kitaplığın `??` işleci, sağ tarafı yalnız sol taraf `nil` ise değerlendirir. Bu, **`@autoclosure`** ile yapılır: parametre bir kapanışa sarılır, ama çağıran sıradan bir ifade yazar. Kendi genel fonksiyonumuzda aynı kalıbı kuruyoruz: `orElse` parametresi yalnız gerektiğinde çağrılır.

```swift
// main.swift — @autoclosure ile genel bir varsayilan deger
func unwrap<T>(_ value: T?, orElse backup: @autoclosure () -> T) -> T {
    if let value { return value }
    return backup()
}

let present = Probe("deger-var")
let absent = Probe("deger-yok")

let known: Int? = 7
let missing: Int? = nil

_ = present.use { unwrap(known, orElse: present.eval(0)) }
_ = present.use { unwrap(known, orElse: present.eval(0)) }
_ = absent.use { unwrap(missing, orElse: absent.eval(0)) }
_ = absent.use { unwrap(missing, orElse: absent.eval(0)) }

let all = [present, absent]
for probe in all { probe.report() }
tally(all)
```

```
deger-var: 0 degerlendirme, 2 kullanim -> hic
deger-yok: 2 degerlendirme, 2 kullanim -> her-kullanimda
hemen: 0
ilk-kullanimda: 0
her-kullanimda: 1
hic: 1
```

Değer varken yedek ifade hiç değerlendirilmedi: `hic`. Değer yokken her çağrıda değerlendirildi: `her-kullanimda`. Genel fonksiyon `T` hakkında hiçbir şey bilmez, ama kapanışın çağrılıp çağrılmayacağına kendi gövdesi karar verir; ertelenen şey tipten bağımsızdır. Bu, dersin ana temasıyla uyumludur: genel bir gövde her çağrıda koşar, ve gövdenin içindeki bir ifade bile koşmayabilir. **TS40.** Ölçüm `Int` ile yapıldı; ertelenen ifadenin yan etkisi olan tiplerde de aynı kalacağı varsayılmadı.

## Genel Yapı Elemanı Saklar

Genel bir yapının içine koyduğun değer, tip parametresi ne olursa olsun, ekleme anında değerlendirilir ve yapı o değeri tutar. `Stack<Int>`'e `append` ile verilen ifade, `append` çağrılmadan önce çağıran tarafta hesaplanır; yığıt yalnız sonucu saklar. Bunu ölçüyoruz, çünkü genel bir kapsayıcının "tembel" ya da "hesaplanan" davranmadığını, yalnız taşıdığını görmek önemlidir.

```swift
// main.swift — genel yapi eklenen degeri saklar
let element = Probe("yigin-elemani")

var holder = Stack<Int>()
holder.append(element.eval(5))

_ = element.use { holder.count }
_ = element.use { holder.count }
_ = element.use { holder.pop() }

element.report()
```

```
yigin-elemani: 1 degerlendirme, 3 kullanim -> hemen
```

İfade bir kez, ekleme satırında değerlendirildi; yığıtı okumak ya da elemanı çekmek onu yeniden hesaplamadı: `hemen`. Genel yapı bu bakımdan bir saklanan özellik gibi davranır. Hesaplanan davranış istiyorsan, yapıya bir değer yerine bir kapanış verirsin, ve bu durumda saklanan şey kapanıştır. **TS41.** Tek bir ekleme ve `Int` ile ölçüldü; yığıtın dolup yeniden boyutlandığı durumlar bu ölçümün dışındadır.

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

Tembel dizinin eleman başına bir dönüştürücü çağrısı vardır, ve çağrı yalnız okunan elemana yapılır. Aşağıdaki dizi iki elemanlıdır. Dönüştürücü, ilk eleman için bir `Probe`'u, ikinci eleman için ötekini kullanır. Dizi yalnız `first` ile okunur.

```swift
// main.swift — ayni tembel dizi, hem her kullanimda hem hic
let hot = Probe("okunan-eleman")
let cold = Probe("okunmayan-eleman")

let pipeline = [10, 20].lazy.map { value -> Int in
    value == 10 ? hot.eval(value + 1) : cold.eval(value + 1)
}

_ = hot.use { pipeline.first }
_ = hot.use { pipeline.first }
_ = cold.use { pipeline.first }
_ = cold.use { pipeline.first }

hot.report()
cold.report()
```

```
okunan-eleman: 4 degerlendirme, 2 kullanim -> her-kullanimda
okunmayan-eleman: 0 degerlendirme, 2 kullanim -> hic
```

Tek bir tembel dizi iki ayrı an taşıdı. İlk eleman her `first` çağrısında yeniden dönüştürüldü. Blokta dört `first` çağrısı var ve dördü de ilk elemanı okur; `hot` yalnız kendi iki kullanımını saydı, ama dört değerlendirmenin hepsini gördü: `her-kullanimda`. İkinci elemana kimse uzanmadı, dönüştürücü ona hiç koşmadı: `hic`, hem de iki kullanıma karşın. Tembel dizi, istekli `map`'in tersini yapar: istekli dizi tüm elemanları hemen hesaplar, tembel dizi hiçbirini önceden hesaplamaz. **TS42.** `first` yalnız ilk elemanı değerlendirdi; bu, koleksiyonun `first` gerçekleştirimine bağlı bir sonuçtur ve Swift 6.4'te ölçüldü.

## Özet

- Genellik, bir gövdeyi tip parametresiyle bir kez yazıp birçok tipte kullanmayı sağlar; kısıt, tipin taşıması gereken protokolü söyler.
- İlişkili tip, protokolün bir tipe ihtiyaç duyduğunu ama onu uyan tipin belirleyeceğini bildirir; `where` ile bu tipe kısıt konur.
- İstekli `map` dönüştürücüyü hemen çağırır; `lazy.map` her erişimde yeniden çağırır.
- Tembel dizinin hiç okunmayan elemanı için dönüştürücü koşmaz; aynı dizi hem `her-kullanimda` hem `hic` taşıyabilir.
- Bu dersin anı: **her kullanımda** (`lazy.map` dönüştürücüsü, aynı elemana iki erişim).

## Sonraki Adım

Şimdiye kadar her gövde sonuna kadar koştu. Peki gövdenin ortasında bir şey ters giderse, geri kalan satırlara ne olur? Sıradaki ders, hata fırlatmayı ve fırlatmadan sonraki ifadelerin değerlendirilmediğini ölçüyor.
