Nesne Yönelimli Programlama (OOP) dünyasına adım attığınızda, yazılımı gerçek dünyadaki modellere uygun, esnek ve yeniden kullanılabilir hale getiren üç temel ilke ile karşılaşırsınız: Sarmalama (Encapsulation), Kalıtım (Inheritance) ve Çokbiçimlilik (Polymorphism). Bu yazımızda, Java programlama dilinin temel yapıtaşlarından biri olan ve kodun yeniden kullanılabilirliğini en üst düzeye çıkaran Kalıtım (Inheritance) konusunu inceleyeceğiz.
1. Kalıtım (Inheritance) Nedir?
Kalıtım, bir nesnenin (veya sınıfın) başka bir nesnenin (veya sınıfın) özelliklerini ve davranışlarını kendi üzerine alması sürecidir. Gerçek dünyada nesneleri sınıflandırırken hiyerarşik bir yapı kullanırız. Örneğin; bir ‘Golden Retriever’ köpektir, köpek bir memelidir, memeli ise bir hayvandır. Bu sınıflandırma sayesinde her nesnenin tüm özelliklerini sıfırdan tanımlamamıza gerek kalmaz; her nesne ortak özelliklerini bir üst kategoriden (atasından) miras alır ve sadece kendisini benzersiz kılan nitelikleri barındırır.
Java terminolojisinde, miras alınan (kendisinden özellik aktarılan) sınıfa Ata Sınıf (Superclass / Base Class), mirası alan (özellikleri devralan) sınıfa ise Alt Sınıf (Subclass / Derived Class) adı verilir. Alt sınıf, ata sınıfın daha özelleşmiş bir versiyonudur; ata sınıfın tanımladığı tüm üyeleri miras alır ve bunlara kendi benzersiz elemanlarını ekler.
2. Sınıflar Arası İlişkiler: “Is-a” İlişkisi
Kalıtım kavramını kodlamadan önce, sınıflar arasındaki ilişkinin mantıksal olarak doğru kurulması çok önemlidir. İki sınıf arasında kalıtım ilişkisi olup olmadığını anlamak için “Her A bir B’dir” (A is a B) önermesi sınanır.
- Örneğin: “Her Otomobil bir Taşıttır” önermesi mantıksal olarak doğrudur. Öyleyse
Otomobilsınıfı ileTasitsınıfı arasında kalıtım ilişkisi vardır veOtomobil,Tasitsınıfından kalıtım yapabilir. - Eğer “Her A’nın bir B’si vardır” (A has a B) önermesi doğruysa (Örn: Her Çemberin bir Merkez Noktası vardır), bu bir kalıtım değil, “içerme (composition)” ilişkisidir.
3. Java’da Kalıtım Nasıl Gerçekleştirilir? (extends Sözcüğü)
Java’da bir sınıfın başka bir sınıftan kalıtım almasını sağlamak için extends anahtar sözcüğü kullanılır. Bir sınıf extends ile başka bir sınıfı miras aldığında, ata sınıfın kurucuları (constructors) hariç tüm nitelik ve yöntemleri alt sınıf için de tanımlı hale gelir.
Basit bir örnek üzerinden gidelim:
// Ata Sınıf (Superclass)
public class Tasit {
public void ilerle(int birim) {
System.out.println("Taşıt " + birim + " birim ilerliyor..");
}
}
// Alt Sınıf (Subclass)
public class Bisiklet extends Tasit {
int vitesSayisi;
}
Yukarıdaki kodda Bisiklet sınıfı, Tasit sınıfından kalıtım almıştır. Bisiklet sınıfının içine ilerle() yöntemini tekrar yazmadığımız halde, Bisiklet nesnesi oluşturduğumuzda bu yöntemi sorunsuzca kullanabiliriz. Çünkü bu yöntem ata sınıftan miras alınmıştır.
4. Kalıtımla Gelen Üyelere Erişim ve protected Kullanımı
Alt sınıf, ata sınıfının tüm üyelerini barındırsa da, ata sınıfta private (gizli) olarak tanımlanmış niteliklere ve yöntemlere doğrudan erişemez. Java’daki sarmalama (encapsulation) ilkesi gereği private üyelere erişim, tanımlandıkları sınıfın dışından tamamen kapalıdır.
Peki, alt sınıfların ata sınıftaki bazı verilere doğrudan erişmesini, ancak dış dünyadan gizli kalmasını nasıl sağlarız? İşte bu noktada protected erişim düzenleyicisi devreye girer. protected olarak tanımlanan nitelik ya da yöntemlere hem o sınıf ile aynı pakette bulunan kodlardan hem de o sınıftan kalıtım alan (doğrudan veya dolaylı) alt sınıflardan doğrudan erişilebilir.
5. super Anahtar Sözcüğü ve Kurucu Zinciri (Constructor Chaining)
Bir alt sınıfın nesnesi oluşturulurken (örneğin new Bisiklet()), bellekte nesne yaratılma süreci içten dışa doğru işler. Nesne oluşturulurken ilk olarak daima ata sınıfın kurucusu (constructor) çalıştırılır. Kurucu zinciri, kalıtım ağacının en tepesinden başlayarak alt sınıfa doğru ilerler.
Alt sınıfın içinden ata sınıfın kurucusunu veya gizlenmiş bir üyesini çağırmak için super anahtar sözcüğü kullanılır. Eğer alt sınıfın kurucusunun içine super() çağrısını açıkça yazmazsanız, Java derleyicisi sizin yerinize ata sınıfın parametresiz (varsayılan) kurucusunu çağıran bir super(); satırını gizlice ekler. Eğer ata sınıfınızın sadece parametre alan bir kurucusu varsa, alt sınıfın kurucusunda super(parametre); yazarak bunu açıkça çağırmak zorundasınız, aksi takdirde derleme hatası alırsınız.
public class KaraTasiti extends Tasit {
protected int tekerlekSayisi;
public KaraTasiti(int tekerlekSayisi) {
this.tekerlekSayisi = tekerlekSayisi;
System.out.println("KaraTasiti kurucusu çalıştı.");
}
}
public class Bisiklet extends KaraTasiti {
public Bisiklet(int tekerlekSayisi) {
super(tekerlekSayisi); // Ata sınıfın parametreli kurucusu çağrılıyor
System.out.println("Bisiklet kurucusu çalıştı.");
}
}
Yukarıdaki Bisiklet nesnesi oluşturulduğunda, super(tekerlekSayisi) kodu sayesinde önce KaraTasiti sınıfının kurucusu çalışacak, ardından Bisiklet kurucusu işlemini tamamlayacaktır.
6. Yöntemleri Geçersiz Kılma (Method Overriding)
Kalıtım ağacında aşağıya (alt sınıflara) doğru inildikçe sınıflar daha da özelleşir. Bazen ata sınıftan miras alınan bir yöntem, alt sınıf için yeterli ya da uygun olmayabilir. Bu durumda, ata sınıftaki yöntemin tamamen aynı adı ve parametre listesini kullanarak alt sınıfta yeniden kodlanmasına Geçersiz Kılma (Method Overriding) adı verilir.
Bu sayede Java, kod çalıştırıldığında (Run-time) hangi nesne üzerinden işlem yapılıyorsa, o nesneye ait olan (özelleştirilmiş) yöntemi bularak çalıştırır. Buna dinamik yöntem sevki (dynamic method dispatch) ve çalışma zamanı çokbiçimliliği (run-time polymorphism) denir.
Örneğin Java’daki tüm sınıflar, hiyerarşinin en tepesinde yer alan Object sınıfından gizlice kalıtım alır. Object sınıfının içinde nesneyi metne çeviren bir toString() yöntemi vardır. Kendi sınıflarınızda toString() yöntemini geçersiz kılarak (override ederek), nesnenizin özelliklerini anlamlı bir metin olarak dışa aktarabilirsiniz.
7. Kalıtımı final ile Engelleme
Eğer tasarladığınız bir sınıfın yapısının başkaları tarafından değiştirilmesini ya da ondan alt sınıflar türetilmesini istemiyorsanız, sınıf tanımının başına final anahtar kelimesini ekleyebilirsiniz. Bir sınıf final olarak işaretlendiğinde, o sınıftan hiçbir sınıf extends ile kalıtım alamaz. Aynı şekilde, sadece belirli bir yöntemin geçersiz kılınmasını (override edilmesini) engellemek istiyorsanız, o yöntemi final olarak tanımlayabilirsiniz; böylece yöntemin davranışı alt sınıflarda kesinlikle değiştirilemez.
Sonuç
Java’da kalıtım (inheritance), yazılımda hiyerarşik ve mantıksal bir düzen kurmanızı sağlar. Kod tekrarını önleyerek halihazırda test edilmiş, güvenilir bir ata sınıftan yeni nesneler türetmenize olanak tanır. extends ile kurulan bu yapıyı super ile besler ve ihtiyacınız olan davranışları yöntem geçersiz kılma (overriding) ile özelleştirirseniz, profesyonel, esnek ve bakımı kolay yazılım projeleri inşa edebilirsiniz. Programlama serüveninizde sınıflarınızı tasarlarken “Hangi nesne neyin alt türüdür?” sorusunu sorarak işe başlamak, kalıtımın gücünü projenize doğrudan yansıtacaktır!





