↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)

3 dk okuma #dart
Dizi · 42/64 Dart Türkçe Tutorial
  1. Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
  2. Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
  3. Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
  4. Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
  5. Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
  6. Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
  7. Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
  8. Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
  9. Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
  10. Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
  11. Bölüm 3 — Konu 11: `if / else if / else`
  12. Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
  13. Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
  14. Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
  15. Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
  16. Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
  17. Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
  18. Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
  19. Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
  20. Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
  21. Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
  22. Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
  23. Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
  24. Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
  25. Bölüm 5 — Konu 26: Recursion (Özyineleme)
  26. Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
  27. Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
  28. Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
  29. Bölüm 6 — Konu 30: Getter / Setter
  30. Bölüm 6 — Konu 31: Statik Üyeler (`static`)
  31. Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
  32. Bölüm 7 — Konu 33: Metod Override Etme, `@override`
  33. Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
  34. Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
  35. Bölüm 7 — Konu 36: Mixin (`with`)
  36. Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
  37. Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
  38. Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
  39. Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
  40. Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
  41. Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
  42. Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
  43. Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
  44. Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
  45. Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
  46. Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
  47. Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
  48. Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
  49. Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
  50. Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
  51. Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
  52. Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
  53. Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
  54. Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
  55. Bölüm 12 — Konu 56: Extension Methods
  56. Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
  57. Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
  58. Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
  59. Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
  60. Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
  61. Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
  62. Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
  63. Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
  64. Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
Dizinin sayfası →
İçindekiler 9 başlık
  1. Sorun: Generic Tip Üzerinde Hiçbir Varsayımda Bulunamazsın
  2. Çözüm: extends ile Generic Sınırlama (Bound)
  3. Sınırlamayı İhlal Etmeye Çalışmak
  4. Kendi Class'larınla Generic Sınırlama Kullanma
  5. Sınırlama Olmadan vs Sınırlama İle — Karşılaştırma
  6. Object — Varsayılan (Örtük) Sınırlama
  7. Birden Fazla Generic Parametrede Sınırlama
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler

Sorun: Generic Tip Üzerinde Hiçbir Varsayımda Bulunamazsın

Bir önceki derste generic bir Kutu<T> yazmıştık. Ama T tamamen "sınırsız" olduğu için, içindeki değer üzerinde hiçbir özel işlem yapamayız:

dart
class Kutu<T> {
  T deger;
  Kutu(this.deger);

  bool digeriylebuyukmu(Kutu<T> diger) {
    return deger > diger.deger; // ❌ HATA! T'nin '>' operatörüne sahip olduğu garanti değil
  }
}

Bu kod derlenmez — çünkü T, herhangi bir tip olabilir (bir String, bir bool, hatta kendi yazdığın bir class). Dart, T'nin > operatörünü desteklediğinden emin olamaz (örneğin bool tipinde > operatörü anlamsızdır). Bu yüzden derleyici, güvenli olmayan bu kodu reddediyor.

Çözüm: extends ile Generic Sınırlama (Bound)

dart
class Kutu<T extends Comparable> {
  T deger;
  Kutu(this.deger);

  bool digeriylebuyukmu(Kutu<T> diger) {
    return deger.compareTo(diger.deger) > 0; // ✅ artık güvenli!
  }
}

void main() {
  Kutu<int> k1 = Kutu(10);
  Kutu<int> k2 = Kutu(20);
  print(k1.digeriylebuyukmu(k2)); // false

  Kutu<String> k3 = Kutu("elma");
  Kutu<String> k4 = Kutu("armut");
  print(k3.digeriylebuyukmu(k4)); // false (alfabetik karşılaştırma)
}

<T extends Comparable>, "T, sadece Comparable interface'ini implemente eden tiplerden biri olabilir" anlamına gelir. Comparable, Dart'ın standart kütüphanesinde tanımlı, compareTo() metoduna sahip olmayı garanti eden bir interface'dir — int, double, String gibi birçok yerleşik tip bunu zaten implemente eder.

Bu sınırlama sayesinde ne kazanıyoruz? Dart artık, T her ne olursa olsun, compareTo() metoduna sahip olacağını garanti eder — bu yüzden deger.compareTo(diger.deger) çağrısı güvenle derlenir.

Sınırlamayı İhlal Etmeye Çalışmak

dart
class Kutu<T extends Comparable> {
  T deger;
  Kutu(this.deger);
}

void main() {
  Kutu<bool> k = Kutu(true); // ❌ HATA! bool, Comparable'ı implemente etmiyor
}

bool, compareTo() metoduna sahip değildir (iki boolean değeri "büyük/küçük" karşılaştırmak mantıklı değildir), bu yüzden Dart, Kutu<bool> oluşturmana derleme zamanında izin vermez. Bu, generic sınırlamanın asıl gücü — yanlış kullanımı, kod çalışmadan önce yakalıyor.

Kendi Class'larınla Generic Sınırlama Kullanma

dart
abstract class Hayvan {
  String isim;
  Hayvan(this.isim);
  void sesCikar();
}

class Kedi extends Hayvan {
  Kedi(String isim) : super(isim);
  @override
  void sesCikar() => print("Miyav!");
}

class HayvanBarinagi<T extends Hayvan> {
  List<T> hayvanlar = [];

  void ekle(T hayvan) {
    hayvanlar.add(hayvan);
  }

  void hepsiniSesletir() {
    for (var hayvan in hayvanlar) {
      hayvan.sesCikar(); // ✅ güvenli, çünkü T kesinlikle bir Hayvan
    }
  }
}

void main() {
  HayvanBarinagi<Kedi> kediBarinagi = HayvanBarinagi();
  kediBarinagi.ekle(Kedi("Tekir"));
  kediBarinagi.hepsiniSesletir(); // Miyav!
}

<T extends Hayvan>, HayvanBarinagi'nin sadece Hayvan'dan türeyen tiplerle (Kedi, Kopek gibi) kullanılabileceğini garanti ediyor. Bu sayede, hepsiniSesletir() metodu içinde hayvan.sesCikar() çağırmak güvenlidir — Dart, T'nin kesinlikle sesCikar() metoduna sahip olduğunu bilir (çünkü Hayvan'ın alt sınıfı olmak zorunda).

dart
void main() {
  HayvanBarinagi<String> stringBarinagi = HayvanBarinagi(); // ❌ HATA! String, Hayvan'dan türemiyor
}

Sınırlama Olmadan vs Sınırlama İle — Karşılaştırma

dart
// Sınırsız generic — hiçbir varsayımda bulunamazsın
class KutuSinirsiz<T> {
  T deger;
  KutuSinirsiz(this.deger);
  // sadece Object'in metodlarını (toString, ==ile vb.) kullanabilirsin
}

// Sınırlı generic — belirli bir "yetenek" garanti edilir
class KutuSinirli<T extends Comparable> {
  T deger;
  KutuSinirli(this.deger);
  // Comparable'ın metodlarını (compareTo) güvenle kullanabilirsin
}

Ne zaman sınırlama kullanılır? Generic tipin, sınıfın içinde belirli bir davranışa (bir metoda, bir alana) sahip olması gerektiğinde. Eğer generic tipi sadece "saklamak" için kullanıyorsan (Bölüm 9 Konu 42'deki basit Kutu<T> gibi), sınırlamaya gerek yoktur. Ama generic tip üzerinde işlem yapman gerekiyorsa (karşılaştırma, belirli bir metod çağırma), sınırlama şarttır.

Object — Varsayılan (Örtük) Sınırlama

Aslında, sınırlama belirtmediğinde bile, Dart örtük olarak <T extends Object?> varsayar (ya da null safety bağlamına göre Object) — yani her generic tip, en azından Object'in (Bölüm 2'de gördüğümüz tip hiyerarşisinin köküdür) sahip olduğu temel metodlara (toString(), == gibi) her zaman sahiptir.

Birden Fazla Generic Parametrede Sınırlama

dart
class Eslesme<K extends Comparable, V> {
  K anahtar;
  V deger;
  Eslesme(this.anahtar, this.deger);
}

Sınırlama, birden fazla tip parametresinin her birine ayrı ayrı uygulanabilir — burada sadece K sınırlandırıldı, V sınırsız bırakıldı.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Sınırlanmamış (<T>) bir generic tip üzerinde, sadece Object'in temel metodlarını güvenle kullanabilirsin.
  • <T extends TipAdı>, generic tipin belirli bir sınıf/interface'den türemesini zorunlu kılar — bu, o tipin belirli metod/alanlara sahip olacağını garanti eder.
  • Sınırlama ihlal edildiğinde (örn. Kutu<bool> ile Comparable sınırlaması), hata derleme zamanında yakalanır.
  • Generic tip üzerinde özel bir davranış (karşılaştırma, belirli bir metod çağırma) gerekiyorsa sınırlama şarttır; sadece "saklamak" için sınırlamaya gerek yoktur.
  • Birden fazla tip parametresinin her biri ayrı ayrı sınırlandırılabilir.

📝 Ödevler

  • [ ] Comparable sınırlamalı bir Kutu<T> sınıfı yaz, iki farklı Kutu<int> nesnesini karşılaştıran bir metod ekle.
  • [ ] Kutu<bool> oluşturmayı deneyip (eğer Comparable sınırlaması varsa) derleme hatasını gözlemle.
  • [ ] Kendi soyut sınıfını (örn. Sekil, alanHesapla() metoduyla) yaz, bu sınıfla sınırlandırılmış generic bir SekilKoleksiyonu<T extends Sekil> sınıfı oluştur, içindeki tüm şekillerin toplam alanını hesaplayan bir metod ekle.
  • [ ] Sınırlanmamış bir generic sınıfta, sınırlı olmadığı için hangi işlemleri yapamayacağını (örn. karşılaştırma, belirli bir metod çağırma) örnekle göster.
  • [ ] İki tip parametreli bir generic sınıf yaz, sadece birini sınırlandır, ikisini de kullanan bir metod ekle.

Sıradaki konu: Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır (List<T>, Map<K,V>)