↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır

3 dk okuma #dart
Dizi · 43/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 10 başlık
  1. List<T> — Basitleştirilmiş Bir Zihin Modeli
  2. operator [] — Küçük Bir Not
  3. Map<K, V> — İki Tip Parametreli Generic
  4. Koleksiyon Tipleri Arasında Uyumluluk — Kovaryans (Kısa Bir Bakış)
  5. is ile Generic Tip Kontrolü
  6. List<Object> vs List<dynamic> — İnce Bir Fark
  7. Neden Bu Konuyu Bilmek Önemli?
  8. 🎯 Bu Dersten Çıkarılması Gerekenler
  9. 📝 Ödevler
  10. 🏁 Bölüm 9 Tamamlandı!

Bölüm 9'un son konusundayız. Şimdiye kadar generics'i kendi sınıflarımızda kullanmayı gördük. Bu derste, Bölüm 4'te öğrendiğimiz List, Set, Map'in aslında birer generic sınıf olarak nasıl tasarlandığını anlayacağız.

List<T> — Basitleştirilmiş Bir Zihin Modeli

Dart'ın gerçek List implementasyonu çok daha karmaşıktır (performans optimizasyonları, farklı liste türleri vb. içerir), ama kavramsal olarak şöyle düşünebilirsin:

dart
// Bu, GERÇEK List implementasyonu DEĞİL — sadece kavramı anlamak için basitleştirilmiş bir örnek
class BenimListem<T> {
  final List<T> _icerik = [];

  void add(T eleman) {
    _icerik.add(eleman);
  }

  T operator [](int index) {
    return _icerik[index];
  }

  int get length => _icerik.length;
}

void main() {
  BenimListem<String> liste = BenimListem();
  liste.add("Merhaba");
  print(liste[0]); // Merhaba
  print(liste.length); // 1
}

Bu örnek, Bölüm 9'da öğrendiğin generic sınıf kavramının, Dart'ın kendi List<T> sınıfında da aynı prensiple çalıştığını gösteriyor. List<String> yazdığında, aslında List sınıfının T tip parametresine String "geçiriyorsun" — tıpkı kendi Kutu<T> sınıfını kullandığın gibi.

operator [] — Küçük Bir Not

Yukarıdaki örnekte T operator [](int index) gördün — bu, operatör overloading dediğimiz, ileri bir Dart özelliğidir ([] erişim operatörünü kendi sınıfın için özelleştirmeni sağlar). Bunu şimdi detaylı işlemiyoruz, sadece liste[0] gibi bir erişimin arka planda bir metod çağrısı olduğunu bilmen, generics'in koleksiyonlarda nasıl işlediğini anlaman için yeterli.

Map<K, V> — İki Tip Parametreli Generic

Bölüm 9 Konu 42'de gördüğümüz Cift<A, B> örneğini hatırlarsan, Map<K, V> de aynı mantıkla çalışır — sadece çok daha gelişmiş bir implementasyona sahiptir (hash tabanlı arama için optimize edilmiştir):

dart
// Kavramsal basitleştirme
class BenimMapim<K, V> {
  final Map<K, V> _icerik = {};

  void set(K anahtar, V deger) {
    _icerik[anahtar] = deger;
  }

  V? get(K anahtar) {
    return _icerik[anahtar];
  }
}

void main() {
  BenimMapim<String, int> yaslar = BenimMapim();
  yaslar.set("Ahmet", 25);
  print(yaslar.get("Ahmet")); // 25
}

K (Key) ve V (Value), Bölüm 9'da öğrendiğimiz "birden fazla tip parametresi" kavramının Dart'ın kendi kütüphanesindeki gerçek kullanımıdır.

Koleksiyon Tipleri Arasında Uyumluluk — Kovaryans (Kısa Bir Bakış)

dart
void main() {
  List<int> tamSayilar = [1, 2, 3];
  List<num> sayilar = tamSayilar; // ✅ çalışır! List<int>, List<num>'a atanabilir
  print(sayilar); // [1, 2, 3]
}

Bu ilginç bir davranış — List<int>, List<num> tipine atanabiliyor, çünkü Bölüm 2'de gördüğümüz gibi int, num'un bir alt tipidir. Bu davranışa kovaryans (covariance) denir — "eğer int, num'un alt tipiyse, List<int> da List<num>'ın alt tipi sayılır" mantığı. Bunu tam olarak Bölüm 13'te (variance detayları) işleyeceğiz — şimdilik sadece bu davranışın var olduğunu ve bazen beklenmedik sonuçlara yol açabileceğini bilmen yeterli:

dart
void main() {
  List<int> tamSayilar = [1, 2, 3];
  List<num> sayilar = tamSayilar;

  sayilar.add(3.14); // ❌ RUNTIME HATASI! Aslında altta yatan liste hâlâ List<int>
}

Bu, ileri bir tuzak — sayilar değişkeni List<num> tipinde görünse de, altında yatan gerçek nesne hâlâ List<int>'tir, bu yüzden ona bir double eklemeye çalışmak çalışma zamanında hataya yol açar. Bu konuyu şimdilik sadece farkında olman için gösteriyoruz; derinlemesine Bölüm 13'te işleyeceğiz.

is ile Generic Tip Kontrolü

dart
void main() {
  List<String> isimler = ["Ahmet", "Ayşe"];
  List<int> sayilar = [1, 2, 3];

  print(isimler is List<String>); // true
  print(isimler is List<int>);    // false
  print(isimler is List);         // true — tip parametresi belirtilmeden de kontrol edilebilir
}

Bölüm 3'te öğrendiğimiz is operatörü, generic koleksiyonlarla da çalışır — hem tam tip eşleşmesi (List<String>) hem de genel tip kontrolü (List) yapabilirsin.

List<Object> vs List<dynamic> — İnce Bir Fark

dart
void main() {
  List<Object> objeListesi = [1, "iki", true]; // karışık tipler, ama hepsi Object
  List<dynamic> dinamikListe = [1, "iki", true]; // aynı görünüyor ama...

  for (var eleman in objeListesi) {
    // eleman, Object tipinde — sadece Object'in metodları doğrudan çağrılabilir
    print(eleman.toString());
  }

  for (var eleman in dinamikListe) {
    // eleman, dynamic tipinde — HERHANGİ bir metod çağrılabilir (riskli!)
    print(eleman.toString());
  }
}

Bölüm 2'de öğrendiğimiz Object ile dynamic arasındaki fark, koleksiyonlarda da aynen geçerlidir: List<Object>, her elemanın en azından Object olduğunu garanti eder (tip güvenli), List<dynamic> ise tip kontrolünü tamamen kapatır. JSON gibi karışık tipli veri yapılarıyla çalışırken, List<dynamic> yerine mümkünse List<Object?> kullanmak daha güvenlidir — ama bu, projenin ihtiyacına göre değişebilir.

Neden Bu Konuyu Bilmek Önemli?

Flutter'da, özellikle API'lerden gelen JSON verisiyle çalışırken (List<dynamic>, Map<String, dynamic> gibi tiplerle sıkça karşılaşacaksın — Bölüm 5'in ilerleyen konularında bunu tam olarak işleyeceğiz), bu generics bilgisi, neden bu tiplerin bu şekilde tasarlandığını ve hangi durumda hangi tipin daha güvenli olduğunu anlamana yardımcı olacak.


🎯 Bu Dersten Çıkarılması Gerekenler

  • Dart'ın List<T>, Map<K, V>, Set<T> koleksiyonları, kendi yazdığın generic sınıflarla aynı temel prensiple tasarlanmıştır.
  • Kovaryans, List<int>'in List<num>'a atanabilmesini sağlar, ama bu bazı runtime hatalarına yol açabilir (Bölüm 13'te derinlemesine işlenecek).
  • is, generic koleksiyonlarla tam tip (List<String>) veya genel tip (List) kontrolü için kullanılabilir.
  • List<Object>, tip güvenliğini korur; List<dynamic>, tip kontrolünü kapatır — JSON gibi karışık verilerle çalışırken bu farkı bilmek önemlidir.

📝 Ödevler

  • [ ] Kendi basit bir generic "liste benzeri" sınıfını yaz (add, length, [] erişimi ile), Bölüm 9 boyunca öğrendiklerini pekiştir.
  • [ ] List<int>'i List<num>'a atayıp, sonra bu değişkene bir double eklemeyi dene, runtime hatasını gözlemle ve yorumla.
  • [ ] is operatörü ile en az 3 farklı generic koleksiyon tipini (List<String>, Map<String, int>, genel List) kontrol eden bir örnek yaz.
  • [ ] Aynı verilerle bir List<Object> ve bir List<dynamic> oluştur, aralarındaki tip güvenliği farkını, bilerek yanlış bir metod çağırarak göster.
  • [ ] Kendi cümlelerinle, "Dart'ın List ve Map sınıfları neden generic olarak tasarlandı" sorusunu, Bölüm 9'da öğrendiklerinle açıkla.

🏁 Bölüm 9 Tamamlandı!

Generics'in temellerini, sınırlamalarını ve Dart'ın kendi koleksiyonlarının bunları nasıl kullandığını öğrendik. Bu, hem kendi esnek ve tip-güvenli sınıflarını yazabilmeni hem de Dart'ın standart kütüphanesini çok daha derinlemesine anlamanı sağladı.

Sıradaki durak Bölüm 10 — Asenkron Programlama: Future, async/await, Stream ile Dart'ın zamanla ilgili (zaman alan, beklemeli) işlemleri nasıl yönettiğini işleyeceğiz — bu, Flutter'da API çağrıları yaparken mutlaka bilmen gereken bir konu.