↑↓ seç · Enter aç · Esc kapat

Mutlu Tekin
Mutlu Tekin
← Yazılar

Bölüm 13 — Konu 60: `Never` Tipi, `covariant` ve Generic Variance Detayları

4 dk okuma #dart
Dizi · 58/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 8 başlık
  1. Never Tipi — "Bu Fonksiyon Asla Normal Şekilde Bitmez"
  2. Never ile void Arasındaki Fark
  3. Kovaryans — Bölüm 9'daki Sözü Tutmak
  4. covariant Anahtar Kelimesi
  5. Ne Zaman covariant Kullanılır?
  6. Kovaryans, Kontravaryans, Invaryans — Kısa Bir Kavramsal Harita
  7. 🎯 Bu Dersten Çıkarılması Gerekenler
  8. 📝 Ödevler

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.

dart
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:

dart
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

dart
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:

dart
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:

dart
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.

dart
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, Map gibi) 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

  • [ ] Never dö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>'i List<num>'a atayıp, .add() ile bir double eklemeyi dene, runtime hatasını gözlemle ve bu derste öğrendiğin "kovaryansın güvensizliği" kavramıyla açıkla.
  • [ ] covariant kullanarak bir override örneği yaz (örn. Hayvan/Kedi senaryosu), 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