Dart’ta Generic Tipler: <T>
Merhaba değerli geliştirici arkadaşlar. Bu yazımda elimden geldiğince Dart’ta Generic Tipler konusunu sizlere anlatmaya çalışacağım.
Bir çoğunuz Flutter ve Dart ile uygulama geliştirmeye başladığınızda, kodun çeşitli yerlerinde gizemli bir şekilde beliren <T>, <E> gibi ifadelere veya List<String>, Map<int, User> gibi tanımlamalara rastlamışsınızdır. Bu sivri parantezler ilk bakışta karmaşık bir süs gibi görünebilir. Ancak onlar, Dart’ın en güçlü özelliklerinden biridir: Generic Tipler.
Bu makalede, genericlerin ne olduğunu, neden var olduklarını ve onları kendi kodlarımızda nasıl bir kullanabileceğimizi anlatmaya çalışacağım.
Her şeyden önce Dart’ta neden böyle bir şeye ihtiyaç duyulmuş bunu anlamak gerek. Burada karşımıza çıkan 2 cevap var.
1-Tip Güvensizliği
Bir koleksiyonun (liste, map vb.) içine ne türde veri koyduğumuzu belirtmezsek, derleyici bize yardım edemez. List içine hem int hem String koyabiliriz. Sonra o listeden bir eleman alıp String metodu (.toUpperCase()) kullanmaya çalıştığımızda, eğer o eleman aslında bir int ise uygulamamız çalışma anında (runtime) çöker. Bu, kodumuzun güvenilmez ve kırılgan olmasına neden olur.
2-Kod Tekrarı
Diyelim ki sadece String tutan bir kutu sınıfı, bir de sadece int tutan başka bir kutu sınıfı yazmak istiyoruz.
class StringKutusu {
String _deger;
StringKutusu(this._deger);
String get deger => _deger;
}
class IntKutusu {
int _deger;
IntKutusu(this._deger);
int get deger => _deger;
}
// Peki ya User nesnesi için? Ya Product için?
// Her veri tipi için aynı mantıkta çalışan bir sınıf mı yazacağız?
// Bu sürdürülebilir değil.Bu durum, “Don’t Repeat Yourself” (Kendini Tekrar Etme) prensibini ihlal eder ve kod tabanımızı şişirir. Kod anlamında teknik borç birikir ve proje bir yerden sonra anlamsız ve karışık bir surete bürünür…
Genericlere neden ihtiyaç var sorusunu yanıtladık. Şimdi gelin genericleri tanıyalım..
<T> Nedir? Tip Parametresini Anlamak
Genericler, bir sınıfın veya fonksiyonun, kullanacağı veri tipini hemen belirtmek yerine, bu tipi bir parametre olarak almasını sağlar. Bu parametre, genellikle <T> ile temsil edilir.
<T>, “Tip için bir Değişken”dir.
Tıpkı bir fonksiyona (int sayi) diyerek bir sayı parametresi verdiğimiz gibi, bir sınıfa da <T> diyerek bir “tip parametresi” vermiş oluruz.
- T ne anlama geliyor? “Type” kelimesinin baş harfi. Bu sadece bir gelenektir. İsterseniz <Elma>, <Foo>, <MyType> da yazabilirsiniz. Ama genel kabul görmüş standartlar şunlardır:
- T (Type): Genel bir tip için.
- E (Element): Bir koleksiyondaki eleman tipi için (List<E>).
- K (Key): Bir Map’teki anahtar tipi için (Map<K, V>).
- V (Value): Bir Map’teki değer tipi için (Map<K, V>).
List<String> Neden Yazıyoruz?
List sınıfı, Dart’ta generic olarak yazılmıştır. Orijinal tanımı şuna benzer: class List<E> { … }.
Biz List<String> myList = []; yazdığımızda, derleyiciye şunu söylemiş oluruz:
“Sevgili derleyici, List kalıbını kullanarak bir liste oluşturuyorum. Bu kalıbın <E> ile belirttiği yere, ben String tipini yerleştiriyorum. Artık myList, sadece ve sadece String kabul eden, tip-güvenli bir listedir.
Bu andan itibaren derleyici bizim koruma meleğimiz olur:
List<String> isimler = [];
isimler.add("Ahmet"); // ✅ Harika, bu bir String.
isimler.add("Ayşe"); // ✅ Süper.
// isimler.add(123); // ❌ HATA! Derleyici anında uyarır:
// "The argument type 'int' can't be assigned to the parameter type 'String'."
// Daha kodu çalıştırmadan hatayı yakaladık!
String ilkIsim = isimler[0]; // Derleyici, bu listeden gelen her şeyin String olduğunu bilir.
print(ilkIsim.toUpperCase()); // ✅ Güvenle kullanabiliriz. Çökme riski yok.Buraya kadar umarım sizler için açık ve anlaşılır olmuştur. Eğer öyle olduysa hadi gelin şimdide kendi genericlerimizi oluşturmaya bakalım.
Kendi Generic Sınıfımızı Yazalım
Burada daha önce yukarıda her tip için ayrı ayrı Kutu sınıfları yazdığımız örneği kullanacağım. Şimdi o problemi genericlerle çözelim.
// Herhangi bir tipte veri tutabilen, tip-güvenli bir Kutu sınıfı.
class Kutu<T> {
T _deger; // _deger'in tipi artık 'T'. Yani ne verirsek o olacak.
Kutu(this._deger); // Kurucu metot, T tipinde bir değer alır.
T get deger => _deger; // Geriye T tipinde bir değer döndürür.
void setDeger(T yeniDeger) { // T tipinde yeni bir değer alır.
_deger = yeniDeger;
}
void icerigiYazdir() {
print('Kutunun içinde ${deger.runtimeType} tipinde şu değer var: $deger');
}
}
void main() {
// 1. String tutan bir Kutu oluşturalım.
// Burada T yerine String geçmiş oldu.
var stringKutusu = Kutu<String>("Merhaba Dünya");
stringKutusu.icerigiYazdir(); // Çıktı: Kutunun içinde String tipinde şu değer var: Merhaba Dünya
String icerik = stringKutusu.deger; // Casting'e (as String) gerek yok!
print(icerik.toUpperCase()); // GÜVENLİ!
// 2. Integer tutan bir Kutu oluşturalım.
// Burada T yerine int geçmiş oldu.
var intKutusu = Kutu<int>(42);
intKutusu.icerigiYazdir(); // Çıktı: Kutunun içinde int tipinde şu değer var: 42
int sayi = intKutusu.deger;
print(sayi + 8); // GÜVENLİ!
// 3. Farklı bir tipte veri atamaya çalışalım.
// stringKutusu.setDeger(100); // ❌ HATA! Derleyici anında engeller.
// Çünkü bu kutu sadece String kabul etmek üzere yaratıldı.
// 4. User nesnesi tutan bir Kutu
var userKutusu = Kutu<User>(User(name: "Ali"));
User ali = userKutusu.deger;
print(ali.name); // GÜVENLİ!
}
class User { String name; User({required this.name}); }Gördüğünüz gibi, tek bir Kutu<T> sınıfı yazarak sonsuz sayıda, farklı tipler için çalışan, üstelik tamamen tip-güvenli sınıflar elde etmiş olduk. Kod tekrarı yok, çalışma zamanı hatası riski minimumda.
Değerli arkadaşlar bu yazımda sizlere Dart’ta Generic Tipleri anlatmaya çalıştım umarım ilgilisi ve meraklısı için faydalı olur.
Sonraki yazılarda görüşmek üzere..
Github: www.github.com/abdullah017
Linkedin: www.linkedin.com/in/abdullahtas
Stackoverflow: https://stackoverflow.com/users/13807726/abdullah-t
#FREEPALESTINA
