Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları
Dizi · 58/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 8 başlık
Bölüm 9 Konu 44'te kovaryansı kısaca görmüş ve "bunu Bölüm 13'te derinlemesine işleyeceğiz" demiştik. Şimdi o sözü tutuyoruz — hem Never tipini hem de variance'ın tam mekaniğini işleyeceğiz.
Never Tipi — "Bu Fonksiyon Asla Normal Şekilde Bitmez"
Bölüm 2'de Dart'ın tip hiyerarşisini görmüştük — her şey Object?'ten türer. Never, bu hiyerarşinin tam tersi uçta, en özel (en "alt") tiptir — hiçbir değerin sahip olamayacağı bir tiptir.
Never hataFirlat(String mesaj) {
throw Exception(mesaj);
}
void main() {
int x = 5;
if (x < 0) {
hataFirlat("x negatif olamaz"); // fonksiyon buradan ASLA geri dönmez
}
print("x pozitif: $x"); // Dart, bu satırın güvenle çalışacağını BİLİR
}Never dönüş tipi, "bu fonksiyon her zaman bir hata fırlatır (veya sonsuz döngüye girer), asla normal şekilde geri dönmez" demektir. Bu, sadece bir dokümantasyon notu değil — Dart'ın akış analizini (flow analysis) etkiler:
String durumBelirle(int? deger) {
if (deger == null) {
hataFirlat("Değer null olamaz");
// Dart burada BİLİR ki bu satırdan sonrası hiç çalışmaz
}
return "Değer: ${deger.toString()}"; // deger burada NON-NULLABLE olarak biliniyor!
}Normalde, deger nullable (int?) olduğu için deger.toString() çağırmak için deger! veya deger?.toString() ?? "..." gibi bir null kontrolüne ihtiyacın olurdu (Bölüm 2'yi hatırla). Ama hataFirlat'ın dönüş tipi Never olduğu için, Dart "bu satırdan sonra kesinlikle deger null değil, çünkü null olsaydı zaten hataFirlat() çalışıp fonksiyon bitmiş olurdu" çıkarımını yapabiliyor. Bu, Bölüm 2'de öğrendiğimiz type promotion (tip daraltma) kavramının, Never tipi sayesinde daha da güçlü hale gelmiş halidir.
Never ile void Arasındaki Fark
void birSeyYap() {
print("Yapıldı");
// fonksiyon normal şekilde BİTER (bir değer döndürmese de)
}
Never sonsuzDongu() {
while (true) {
// asla bitmez
}
}void, "bu fonksiyon bir değer döndürmez ama normal şekilde biter" demektir. Never, "bu fonksiyon hiçbir zaman normal şekilde bitmez" demektir (ya hata fırlatır ya da sonsuza kadar çalışır). Bu ince ama önemli bir farktır — void dönen bir fonksiyonun ardından yazılan kod her zaman çalışır, Never dönen bir fonksiyonun ardından yazılan kod asla çalışmaz (Dart bunu bilir).
Kovaryans — Bölüm 9'daki Sözü Tutmak
Bölüm 9 Konu 44'te şunu görmüştük:
void main() {
List<int> tamSayilar = [1, 2, 3];
List<num> sayilar = tamSayilar; // ✅ çalışır
sayilar.add(3.14); // ❌ RUNTIME HATASI
}Şimdi bunun tam olarak neden böyle çalıştığını açıklayalım. List<int>'in List<num>'a atanabilmesine kovaryans (covariance) denir. Dart'ın generic koleksiyonları, varsayılan olarak kovaryanttır — yani int, num'ın alt tipiyse, List<int> de List<num>'ın "alt tipi" gibi davranır.
Bu neden riskli? Çünkü List<num> tipindeki bir değişkenin, gerçekte List<int>'i işaret ettiğini derleme zamanında bilemezsin. sayilar.add(3.14) yazdığında, Dart derleme zamanında "bu geçerli, çünkü 3.14 bir num'dır" der — ama çalışma zamanında, altta yatan gerçek liste hâlâ List<int> olduğu için, bir double eklemeye çalışmak hata verir. Buna "kovaryansın güvensizliği" (unsoundness of covariance) denir — teorik olarak tip sistemi bunu tam olarak engelleyemez, bu yüzden Dart bunu çalışma zamanı kontrolüyle yakalar.
covariant Anahtar Kelimesi
Bölüm 7'de metod override etmeyi öğrenmiştik — override edilen bir metodun parametre tipleri, üst sınıfla uyumlu olmalı demiştik. Bazen, bir alt sınıfta, override edilen bir parametreyi daha spesifik bir tipe daraltmak istersin — bu normalde tip güvenliği açısından tehlikelidir, ama bazı durumlarda mantıklıdır. covariant, bunu bilerek ve açıkça yapmanı sağlar:
class Hayvan {
void beslenirken(Hayvan digerHayvan) {
print("Hayvanlar besleniyor");
}
}
class Kedi extends Hayvan {
@override
void beslenirken(covariant Kedi digerHayvan) { // parametre tipini DARALTTIK
print("Kediler mama yiyor");
}
}
void main() {
Kedi kedi1 = Kedi();
Kedi kedi2 = Kedi();
kedi1.beslenirken(kedi2); // ✅ çalışır
}covariant olmadan, Kedi.beslenirken'in parametresini Hayvan yerine Kedi yapmak derleme hatası verirdi (çünkü Bölüm 7'de öğrendiğimiz "override edilen metodun imzası üst sınıfla uyumlu olmalı" kuralını ihlal eder). covariant anahtar kelimesi, Dart'a "bu daralmayı bilerek yapıyorum, riski kabul ediyorum" demenin yoludur — Dart, bu durumda çalışma zamanında bir tip kontrolü ekler (tıpkı List<num>/List<int> örneğinde olduğu gibi), yanlış bir tip geçilirse runtime'da hata verir.
void main() {
Hayvan hayvan = Kedi();
Hayvan digerHayvan = Hayvan(); // Kedi DEĞİL, sade bir Hayvan
hayvan.beslenirken(digerHayvan); // ❌ RUNTIME HATASI! covariant kontrolü devreye girer
}Ne Zaman covariant Kullanılır?
covariant, nadiren kullanılan, ileri bir özelliktir. En yaygın kullanım alanı, Flutter'ın kendi iç kod tabanında (örneğin bazı RenderObject metodlarında) görülür — kendi günlük kodunda muhtemelen çok az kullanacaksın, ama Flutter'ın (veya başka bir kütüphanenin) kaynak kodunu okurken bu anahtar kelimeyle karşılaştığında, ne anlama geldiğini bilmen önemlidir.
Kovaryans, Kontravaryans, Invaryans — Kısa Bir Kavramsal Harita
Bu terimleri sadece duyman için, çok kısa bir özet (derinlemesine tip teorisi bu tutorial'ın kapsamı dışında):
- Kovaryans:
List<int>→List<num>yönünde "genişleme" kabul edilir (Dart'ın koleksiyonlarının varsayılan davranışı). - Kontravaryans: Fonksiyon parametrelerinde, ters yönde bir uyumluluk kabul edilebilir (örneğin
void Function(num),void Function(int)'in yerine geçebilir) — bu, fonksiyon tipleriyle ilgili ileri bir konudur. - Invaryans: Hiçbir yönde otomatik uyumluluk kabul edilmez — tipin tam olarak eşleşmesi gerekir.
Dart, generic koleksiyonlarda kovaryansı tercih eder (kullanım kolaylığı için), ama bunun çalışma zamanı güvenlik kontrolleriyle desteklenmesi gerektiğini bu derste gördün.
🎯 Bu Dersten Çıkarılması Gerekenler
Never, bir fonksiyonun asla normal şekilde bitmediğini (her zaman hata fırlattığını veya sonsuza kadar çalıştığını) belirtir; bu, Dart'ın akış analizini ve type promotion'ı güçlendirir.void, fonksiyonun normal bittiğini ama değer döndürmediğini;Never, fonksiyonun hiç bitmediğini belirtir.- Dart'ın generic koleksiyonları (
List,Mapgibi) kovaryanttır —List<int>,List<num>'a atanabilir, ama bu çalışma zamanı hatalarına yol açabilir. covariant, bir override edilen metodun parametresini bilerek daha spesifik bir tipe daraltmanı sağlar; Dart bunu çalışma zamanı kontrolüyle güvenli hale getirir.- Bu özellikler nadiren günlük kodda kullanılır, ama Flutter'ın iç kod tabanını ve ileri seviye tip sistemi tartışmalarını anlamak için değerlidir.
📝 Ödevler
- [ ]
Neverdönüş tipli bir hata fırlatma fonksiyonu yaz, bunu bir null kontrolünde kullanarak type promotion'dan faydalan (fonksiyondan sonra nullable bir değişkenin non-nullable olarak davranmasını sağla). - [ ]
List<int>'iList<num>'a atayıp,.add()ile birdoubleeklemeyi dene, runtime hatasını gözlemle ve bu derste öğrendiğin "kovaryansın güvensizliği" kavramıyla açıkla. - [ ]
covariantkullanarak bir override örneği yaz (örn.Hayvan/Kedisenaryosu), hem doğru hem yanlış tiple çağırarak davranışı gözlemle. - [ ] Kendi cümlelerinle, "Never ile void arasındaki fark nedir" sorusunu, akış analizi bağlamında açıkla.
- [ ] Kovaryans, kontravaryans, invaryans terimlerini kendi cümlelerinle özetleyen kısa bir not yaz (derinlemesine tip teorisi gerekmez, sadece temel farkı anlaman yeterli).
Sıradaki konu: Bölüm 13 — Konu 61: Event Loop Derinlemesine — Microtask Queue vs Event Queue