Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
Dizi · 41/64 Dart Türkçe Tutorial
- Bölüm 1 — Konu 1: Dart Nedir, Nerede Kullanılır, Neden Flutter Bu Dili Seçti
- Bölüm 1 — Konu 2: Ortam Kurulumu (Dart SDK, DartPad, Terminal ile Çalıştırma)
- Bölüm 1 — Konu 3: İlk Program (`main()`, `print()`, Dosya Yapısı)
- Bölüm 1 — Konu 4: Yorum Satırları, Temel Sözdizimi Kuralları
- Bölüm 2 — Konu 5: Değişken Tanımlama — `var`, `final`, `const` Farkı
- Bölüm 2 — Konu 6: Temel Tipler — `int`, `double`, `String`, `bool`
- Bölüm 2 — Konu 7: Tip Çıkarımı (Type Inference) ve Açık Tip Belirtme
- Bölüm 2 — Konu 8: `dynamic` ve `Object` — Ne Zaman Kullanılır, Ne Zaman Kaçınılır
- Bölüm 2 — Konu 9: Null Safety Temelleri (`?`, `!`, `late`'e Giriş)
- Bölüm 3 — Konu 10: Aritmetik, Atama, Karşılaştırma, Mantıksal Operatörler
- Bölüm 3 — Konu 11: `if / else if / else`
- Bölüm 3 — Konu 12: `switch` / `switch expression` (Modern Dart)
- Bölüm 3 — Konu 13: Ternary Operatör, `??`, `??=`, `?.`
- Bölüm 3 — Konu 14: `for`, `while`, `do-while` Döngüleri
- Bölüm 4 — Konu 16: `List` — Oluşturma, Erişim, Temel Metodlar
- Bölüm 4 — Konu 17: `Set` — Benzersiz Eleman Mantığı
- Bölüm 4 — Konu 18: `Map` — Key-Value Yapılar
- Bölüm 4 — Konu 19: Koleksiyon Üzerinde `for-in`, `forEach`
- Bölüm 4 — Konu 20: Spread Operatörü (`...`, `...?`) ve Collection If/For
- Bölüm 5 — Konu 21: Fonksiyon Tanımlama, Parametreler (Positional, Named, Optional)
- Bölüm 5 — Konu 22: Varsayılan Parametre Değerleri (Derinlemesine)
- Bölüm 5 — Konu 23: Arrow Function (`=>`) Sözdizimi
- Bölüm 5 — Konu 24: Fonksiyonlar Birinci Sınıf Vatandaş — Değişkene Atama, Parametre Olarak Geçme
- Bölüm 5 — Konu 25: Anonim Fonksiyonlar ve Closure Kavramı (Derinlemesine)
- Bölüm 5 — Konu 26: Recursion (Özyineleme)
- Bölüm 6 — Konu 27: Class Tanımlama, Constructor (Varsayılan, Named, Factory)
- Bölüm 6 — Konu 28: Alanlar (Fields), Metodlar, `this` Kullanımı (Derinlemesine)
- Bölüm 6 — Konu 29: Initializer List, Constructor Kısayolları (Derinlemesine)
- Bölüm 6 — Konu 30: Getter / Setter
- Bölüm 6 — Konu 31: Statik Üyeler (`static`)
- Bölüm 7 — Konu 32: Kalıtım (`extends`), `super` Kullanımı
- Bölüm 7 — Konu 33: Metod Override Etme, `@override`
- Bölüm 7 — Konu 34: Soyut Sınıflar (`abstract class`)
- Bölüm 7 — Konu 35: Interface Mantığı (`implements`)
- Bölüm 7 — Konu 36: Mixin (`with`)
- Bölüm 7 — Konu 37: `enum` — Basit ve Gelişmiş (Metotlu Enum'lar)
- Bölüm 8 — Konu 38: `try / catch / finally`, `throw`
- Bölüm 8 — Konu 39: Özel Exception Sınıfları Yazma
- Bölüm 8 — Konu 40: Sound Null Safety Derinlemesine — `late`, `required`, Non-Nullable Varsayılan Davranış
- Bölüm 8 — Konu 41: `assert` ile Geliştirme Zamanı Kontrolleri
- Bölüm 9 — Konu 42: Generic Sınıflar ve Fonksiyonlar
- Bölüm 9 — Konu 43: Generic Sınırlamalar (`<T extends ...>`)
- Bölüm 9 — Konu 44: Dart'ın Built-in Generic Koleksiyonları Nasıl Çalışır
- Bölüm 10 — Konu 45: `Future` Kavramı, `async`/`await`
- Bölüm 10 — Konu 46: `Future.then`, Hata Yönetimi (`catchError`)
- Bölüm 10 — Konu 47: `Stream` Temelleri, `async*`, `yield`
- Bölüm 10 — Konu 48: `StreamController`, Broadcast Stream
- Bölüm 11 — Konu 49: `map`, `where`, `reduce`, `fold`
- Bölüm 11 — Konu 50: `sort`, Custom Comparator ile Sıralama
- Bölüm 11 — Konu 51: Iterable Kavramı Derinlemesine (Lazy Evaluation)
- Bölüm 11 — Konu 52: Cascade Notasyonu (`..`)
- Bölüm 12 — Konu 53: `pubspec.yaml`, pub.dev'den Paket Ekleme
- Bölüm 12 — Konu 54: Kütüphane Oluşturma, `import`/`export`, `part`/`part of`
- Bölüm 12 — Konu 55: Dart'ta Test Yazımı (`test` Paketi, Unit Test Mantığı)
- Bölüm 12 — Konu 56: Extension Methods
- Bölüm 12 — Konu 57: Dart'ın Derleme Modelleri (JIT vs AOT) ve Bunun Flutter'a Etkisi
- Bölüm 13 — Konu 59: Dart 3 Pattern Matching — Records, Destructuring, Sealed Classes, Exhaustive Switch
- Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
- Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue
- Bölüm 13 — Konu 62: Memory Model & Performans — Garbage Collection, `const` Nesnelerin Bellek Avantajı
- Bölüm 13 — Konu 63: Isolate'ler Arası Mesajlaşmanın Maliyeti ve `compute()`'un İç İşleyişi
- Bölüm 13 — Konu 64: FFI (Foreign Function Interface) — C Koduna Erişim
- Bölüm 13 — Konu 65: Sunucu Tarafında Dart — `dart:io`, `shelf` Paketiyle Basit Bir Backend/CLI Aracı Yazma
- Bölüm 13 — Konu 66: Derleyici & Analiz Araçları — `dart analyze`, Custom Lint Kuralları, `build_runner` Mimarisi
İçindekiler 9 başlık
Bölüm 9'a hoş geldin. Bu bölüm, aslında Bölüm 4'te List<String>, Map<String, int> yazarken sürekli kullandığın <...> sözdiziminin arkasında ne olduğunu açıklayacak — generics.
Generics Neden Var? — Problemi Anlamak
Diyelim ki, herhangi bir tipte tek bir değer tutan basit bir "kutu" sınıfı yazmak istiyorsun:
// Yaklaşım 1: dynamic kullanmak
class KutuDynamic {
dynamic icerik;
KutuDynamic(this.icerik);
}
void main() {
KutuDynamic k = KutuDynamic("Merhaba");
String metin = k.icerik; // ❌ HATA! dynamic, String'e otomatik dönüşmez, tip güvenliği yok
}Bölüm 2'de dynamic'ten kaçınmayı öğrenmiştik — bu yaklaşım tip güvenliğini tamamen kaybettiriyor. Peki her tip için ayrı bir sınıf mı yazmalıyız?
// Yaklaşım 2: Her tip için ayrı sınıf — kod tekrarı!
class StringKutu {
String icerik;
StringKutu(this.icerik);
}
class IntKutu {
int icerik;
IntKutu(this.icerik);
}
class BoolKutu {
bool icerik;
BoolKutu(this.icerik);
}Bu çalışır ama korkunç bir kod tekrarı — her yeni tip için yeni bir sınıf yazman gerekiyor. Generics, tam olarak bu problemi çözmek için var: hem tip güvenliğini koru, hem de kod tekrarını önle.
Generic Sınıf Yazma
class Kutu<T> {
T icerik;
Kutu(this.icerik);
void goster() {
print("İçerik: $icerik, Tip: ${icerik.runtimeType}");
}
}
void main() {
Kutu<String> stringKutu = Kutu<String>("Merhaba");
Kutu<int> intKutu = Kutu<int>(42);
Kutu<bool> boolKutu = Kutu<bool>(true);
stringKutu.goster(); // İçerik: Merhaba, Tip: String
intKutu.goster(); // İçerik: 42, Tip: int
}<T> ne anlama geliyor? T, bir tip parametresidir (type parameter) — "bu sınıf, kullanıldığı anda belirlenecek herhangi bir tip ile çalışabilir" anlamına gelir. T harfi bir kural değil, sadece yaygın bir konvansiyondur ("Type" kelimesinin kısaltması) — istersen Kutu<X> da yazabilirdin, ama T kullanmak yaygın ve okunması kolay bir tercihtir.
Tip güvenliği nasıl korunuyor?
void main() {
Kutu<String> stringKutu = Kutu<String>("Merhaba");
String metin = stringKutu.icerik; // ✅ çalışır! icerik zaten String olarak biliniyor
stringKutu.icerik = 42; // ❌ HATA! Kutu<String>, sadece String kabul eder
}Bu, generics'in gücünü gösteriyor: Kutu<String> dediğinde, Dart artık o kutunun kesinlikle String tutacağını bilir ve tip güvenliğini tam olarak korur — dynamic kullanmadığımız için hiçbir tip belirsizliği yok.
Tip Çıkarımı Generics'te de Çalışır
void main() {
var kutu = Kutu("Merhaba"); // <String> yazmadık, Dart otomatik anladı
print(kutu.icerik.runtimeType); // String
}Bölüm 2'de öğrendiğimiz tip çıkarımı, generics'te de geçerlidir — Kutu("Merhaba") yazdığında, Dart parametrenin String olduğunu görüp T'yi otomatik olarak String olarak belirler. Yine de, özellikle karmaşık durumlarda açıkça Kutu<String>(...) yazmak okunabilirlik açısından tercih edilir.
Generic Fonksiyonlar
Sadece sınıflar değil, fonksiyonlar da generic olabilir:
T ilkEleman<T>(List<T> liste) {
return liste[0];
}
void main() {
List<String> isimler = ["Ahmet", "Ayşe"];
List<int> sayilar = [10, 20, 30];
print(ilkEleman(isimler)); // Ahmet — T, String olarak belirlendi
print(ilkEleman(sayilar)); // 10 — T, int olarak belirlendi
}ilkEleman<T>(List<T> liste), herhangi bir tipteki listenin ilk elemanını, o listenin tipini koruyarak döndürür. Eğer bunu generic olmadan, dynamic ile yazsaydık:
dynamic ilkElemanDynamic(List liste) {
return liste[0];
}
void main() {
List<String> isimler = ["Ahmet", "Ayşe"];
String isim = ilkElemanDynamic(isimler); // ❌ HATA! dynamic, String'e otomatik atanamaz
}Generic versiyon, çağrıldığı listenin gerçek tipini koruyarak döndürüyor — bu, dynamic versiyonunun kaybettiği tip güvenliğini tam olarak sağlıyor.
Birden Fazla Tip Parametresi
class Cift<A, B> {
A birinci;
B ikinci;
Cift(this.birinci, this.ikinci);
@override
String toString() => "($birinci, $ikinci)";
}
void main() {
Cift<String, int> yasBilgisi = Cift("Ahmet", 25);
print(yasBilgisi); // (Ahmet, 25)
Cift<String, bool> aktiflikBilgisi = Cift("Sistem", true);
print(aktiflikBilgisi); // (Sistem, true)
}Bu, aslında Bölüm 4'te sürekli kullandığımız Map<K, V>'nin tam olarak nasıl çalıştığının basitleştirilmiş bir versiyonu — Map, içeride iki tip parametresi alan (K: key tipi, V: value tipi) generic bir sınıftır.
Generic Metod (Sınıf Generic Olmasa Bile)
class Yardimci {
T ikisindenBuyuguGetir<T extends Comparable>(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
}Burada Yardimci sınıfının kendisi generic değil, ama içindeki bir metod generic — bu, sadece o metod çağrıldığında tip parametresi belirlenecek anlamına gelir. T extends Comparable kısmını (generic sınırlama) bir sonraki derste tam olarak işleyeceğiz.
Dart'ın Kendi Koleksiyonları — Artık Nasıl Çalıştıklarını Biliyorsun
List<String> isimler = []; // List<T>, T=String
Map<String, int> yaslar = {}; // Map<K, V>, K=String, V=int
Set<int> sayilar = {}; // Set<T>, T=intBölüm 4'te bunları "kullanmayı" öğrenmiştik, şimdi neden bu şekilde tasarlandıklarını anlıyorsun — List, Map, Set sınıflarının hepsi generic olarak yazılmış, bu sayede herhangi bir tipte veri tutabiliyorlar, ama her kullanımda tam tip güvenliği koruyorlar.
🎯 Bu Dersten Çıkarılması Gerekenler
- Generics,
dynamickullanmadan (tip güvenliğini kaybetmeden) hem esnek hem tip-güvenli kod yazmanı sağlar. <T>, bir tip parametresidir — sınıf/fonksiyon kullanıldığı anda gerçek bir tiple değiştirilir.- Generic sınıflarda tip çıkarımı çalışır, ama açıkça belirtmek genelde daha okunabilirdir.
- Fonksiyonlar da generic olabilir (
T fonksiyon<T>(...)), farklı tiplerle aynı mantığı tekrar yazmadan çalıştırabilirsin. - Birden fazla tip parametresi (
<A, B>) kullanılabilir —Map<K, V>buna bir örnektir. - Dart'ın kendi
List,Map,Setkoleksiyonları, generics kullanılarak tasarlanmıştır.
📝 Ödevler
- [ ] Herhangi bir tipte eleman tutabilen generic bir
Kutu<T>sınıfı yaz, en az 3 farklı tiple (String, int, bool) kullan. - [ ] Bir listenin son elemanını döndüren generic bir fonksiyon yaz (
sonEleman<T>(List<T> liste)), farklı tipte listelerle test et. - [ ]
Cift<A, B>benzeri, iki farklı tipte değer tutan generic bir sınıf yaz, en az iki farklı tip kombinasyonuyla kullan. - [ ] Generic olmayan (dynamic kullanan) bir fonksiyon ile aynı işi yapan generic bir fonksiyonu karşılaştırmalı olarak yaz, tip güvenliği farkını göster.
- [ ] Kendi
Cift<A, B>sınıfına,AveBdeğerlerini yer değiştiren birtakasEt()metodu ekle (yeni birCift<B, A>döndürsün).
Sıradaki konu: Bölüm 9 — Konu 43: Generic Sınırlamalar (<T extends ...>)