Dart'ta Kalıtım (Inheritance) ve Polymorphism - Baba-Çocuk İlişkisi
Kalıtım ve Polymorphism ile Boğuşmam
Bugün Dart'ta nesne yönelimli programlamanın (OOP) kalbine dokundum: Kalıtım (Inheritance) ve Polymorphism. Başta biraz karmaşık gelse de örnekler üzerinde çalışınca gayet mantıklı gelmeye başladı.
Canlı Demo: Inheritance ve Polymorphism
Kalıtım ve polymorphism kavramlarını interaktif olarak deneyin:
Kalıtım (Inheritance) Nedir?
Kalıtımı en basit haliyle şöyle açıklayabilirim: Baba-çocuk ilişkisi. Babanın özellikleri çocuğa geçer. Dart'ta buna extends diyoruz.
SuperClass ve SubClass
- SuperClass: Üst sınıf, baba sınıf
- SubClass: Alt sınıf, çocuk sınıf
Çocuk (SubClass), babasından (SuperClass) özellikler alır ama babası çocuktan alamaz. Tek yönlü bir ilişki yani.
İlk Kalıtım Örneğim: Ev Ailesi
class Ev {
late int penceresayisi;
Ev(this.penceresayisi);
}
class Saray extends Ev {
int? kulesayisi;
Saray(this.kulesayisi, int penceresayisi) : super(penceresayisi);
}
class Villa extends Ev {
bool? garajvarmi;
Villa(this.garajvarmi, int penceresayisi) : super(penceresayisi);
}
void main() {
var ev1 = Ev(4);
print("Normal ev: ${ev1.penceresayisi} pencere");
var saray1 = Saray(3, 50);
print("Saray: ${saray1.kulesayisi} kule, ${saray1.penceresayisi} pencere");
var villa1 = Villa(true, 12);
print("Villa: Garaj var mı? ${villa1.garajvarmi}, ${villa1.penceresayisi} pencere");
}Burada şunu fark ettim: Saray ve Villa sınıfları Ev sınıfından türüyor. Her ikisinde de penceresayisi var çünkü bunu babadan (Ev) alıyorlar. Ama her birinin kendine özgü özellikleri var (kule sayısı, garaj).
Super Anahtar Kelimesi
super() babanın constructor'ını çağırıyor. Yani "baba, senin özelliklerin için gerekli parametreleri buraya verdim" diyoruz.
@override - Çocuk Babasının Metodunu Değiştirebilir
Override konusu çok ilginç. Çocuk, babasından gelen bir metodu beğenmeyebilir ve kendi versiyonunu yazabilir.
Hayvan Sesleri Örneği
Bu örneği yaparken çok eğlendim:
class Hayvan {
void sescikar() {
print("Ses yok");
}
}
class Memeli extends Hayvan {}
class Kedi extends Memeli {
@override
void sescikar() {
print("Miyav");
}
}
class Kopek extends Memeli {
@override
void sescikar() {
print("Hav hav");
}
}
class Tilki extends Memeli {
// Tilkinin sesi yok, babasından alacak
}
void main() {
var kedi1 = Kedi();
var kopek1 = Kopek();
var tilki1 = Tilki();
var hayvan1 = Hayvan();
kedi1.sescikar(); // Miyav
kopek1.sescikar(); // Hav hav
tilki1.sescikar(); // Ses yok (babadan aldı)
hayvan1.sescikar(); // Ses yok
}Override'ın Mantığı
Kedi ve Köpek kendi seslerini tanımladılar (override ettiler). Ama Tilki için özel bir ses tanımlamadık, o yüzden üst sınıfın metodunu kullandı.
Dart şöyle çalışıyor:
- Önce kendi sınıfına bakıyor, metod var mı?
- Yoksa bir üstteki sınıfa bakıyor
- Orada da yoksa daha üsttekine bakıyor
- En üstte bile yoksa hata veriyor
Polymorphism - Kafam Biraz Karıştı
Polymorphism konusunu tam anlamadığımı söylemeliyim. Ama şu kadarını kavradım: Bir babanın tipinde, çocuğun davranışını gösterebiliyoruz.
Hayvan h = Kedi();
h.sescikar(); // "Miyav" yazarBurada h değişkeni Hayvan tipinde ama aslında bir Kedi nesnesi. Bu da çağırdığımızda Kedi'nin sesini çıkarıyor.
Neden Kullanıyoruz?
Araştırdığım kadarıyla şu durumlarda işe yarıyor:
- Liste yapılarında farklı alt sınıfları aynı listede tutmak
- Fonksiyonlara parametre gönderirken esneklik sağlamak
void hayvanSesiCikar(Hayvan hayvan) {
hayvan.sescikar();
}
void main() {
hayvanSesiCikar(Kedi()); // Miyav
hayvanSesiCikar(Kopek()); // Hav hav
hayvanSesiCikar(Tilki()); // Ses yok
}Fonksiyon Hayvan tipinde parametre alıyor ama ona hem Kedi, hem Köpek, hem Tilki gönderebiliyoruz. İşte bu polymorphism.
Tip Kontrolü ve Casting
Bir nesnenin hangi tipte olduğunu kontrol etmek için is kullanıyoruz:
var saray1 = Saray(3, 50);
if (saray1 is Saray) {
print("Bu bir saray");
}
if (saray1 is Ev) {
print("Bu aynı zamanda bir ev"); // Bu da çalışır!
}Upcasting ve Downcasting
Bu isimleri ilk duyduğumda "ne alakası var" dedim ama mantığını anladım:
Upcasting: Çocuğu babaya çevirmek (otomatik olur)
Ev ev = saray1; // Saray'ı Ev tipine çevirdikDowncasting: Babayı çocuğa çevirmek (zorla yapılır)
Ev ev1 = Ev(10);
Villa yeniVilla = ev1 as Villa; // Zorla Villa'ya çevirdikUyarı: Downcasting risklidir! Eğer ev1 gerçekten bir Villa değilse hata alırsınız.
Bugün Öğrendiklerim
- Kalıtım ile kod tekrarını önlüyoruz
- @override ile babadan gelen metodları değiştirebiliyoruz
- Polymorphism esneklik sağlıyor (tam anlayamadım ama)
- is ile tip kontrolü yapabiliyoruz
- Upcasting ve Downcasting ile tipler arası geçiş yapıyoruz
Polymorphism konusunu daha iyi anlamak için daha fazla pratik yapmam gerekiyor. Yarın Interface'ler ve Abstract class'lara bakacağım.
Flutter'da Nerede Kullanıyoruz?
Flutter'da widget'lar da aslında kalıtım kullanıyor:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}StatelessWidget bizim SuperClass'ımız, MyWidget ise SubClass.
Soru ve önerileriniz için:
Bir sonraki yazıda görüşmek üzere!