Miras (inheritance), mevcut bir sınıfa dayalı olarak yeni bir sınıf tanımlamayı sağlayan nesneye yönelik programlamanın temel özelliklerinden biridir. Yazıda sıklıkla karşılaşacağınız terimlerin tanımları ile başlayalım..
Terminoloji:
Inheritance’ta ana fikir, yeni alanlar ve yeni yöntemler ekleyerek veya mevcut yöntemleri yeniden tanımlayarak alt bir sınıf türetmek için mevcut bir sınıfı iyileştirmektir. Alt sınıflar, mevcut sınıfın tüm alanlarını ve yöntemlerini devraldığı için yeniden kullanılmasını sağlar. Orijinal sınıfa üst sınıf, üst sınıfa dayalı sınıfa ise alt sınıf denir. Her sınıf, Java’daki Object sınıfından dolaylı olarak (implicitly) miras alır. Her sınıf bir nesnedir. Object sınıfının içinde alan yoktur, ancak equals ve toString gibi yöntemler vardır. Tüm sınıflar, hiyerarşinin en üstünde Object olacak şekilde bir hiyerarşide (ağaç) düzenlenir.
Java’da kullanılan inheritance (miras) anahtar sözcüklerini (keywords) inceleyelim.
Extends: Başka bir sınıfı extend eden (genişleten) bir sınıf, üst sınıfın (superclass) tüm alanlarını ve yöntemlerini (construct’lar dışında) devralan bir alt sınıftır (subclass). Alt sınıfın (subclass) public ve protected olan tüm alanlara doğrudan erişimi vardır. Alt sınıf, yeni uygulamalarla (aynı imzayı kullanarak) devralınan yöntemlerin tanımlarını geçersiz kılabilir ve super anahtar sözcüğünü kullanarak geçersiz kılınan yöntemlere erişebilir.
Aşağıda bir superclass örneği görülebilmektedir:
Başka bir üst sınıf belirtilmemişse, bir üst sınıf otomatik olarak Object sınıfından miras alır.
Aşağıda bir subclass örneği görülebilmektedir:
Alt sınıfın; alt sınıfı üst sınıftan ayıran alanları ve metodları tanımlaması gerekir. Örneğin, SavingsAccount yani alt sınıf, balance alanını ve metodları BankAccount sınıfından devralır. Constructor devralınmaz.
Peki neden Java’da constructor inherit edilemez?
Constructor’lar sınıf adı ile aynı ada sahiptir. Bu nedenle, constructor’lar alt sınıfta miras alınmışsa, alt sınıf; üst sınıfın adına sahip constructor’ı içerecek. Bu durumda constructor’ın sınıf adıyla aynı ada sahip olması kuralı çiğnenecektir. Güvenlik açısından da bakılacak olursa constructor’ların inherit edilebilmesi halinde, encapsulation işlemi başarısız olurdu. Çünkü bir üst sınıfın constructor’ını kullanarak bir sınıfın özel üyelerine erişebiliriz.
Subclass ve inheritance ilişkisi
Alt sınıf üst sınıftan miras aldığında temelde iki işlemi yürütebilir:
Constructor ve superclass ilişkisi
Subclass’ın kendine ait constructor’ı ve kendine ait private değişkenleri olabilir. Alt sınıfın constructor’ları yalnızca alt sınıfın değişkenlerini initialize edebilir. Bu sebeple, alt sınıfa ait bir değişken initialize edildiğinde, initialize edilen değişken otomatik olarak üst sınıfa ait constructor’lardan birini (default veya parametreleri constructolar) yürütmelidir.
Bir üst sınıfın yapıcısını çağırmak için super anahtar sözcüğü kullanılır.
Aşağıdaki görselde bulunan örnekte subclass’ın constructor’ı içerisindeki super() methodu, superclass’a ait constructor’ı çağırır.
Subclass içerisinde yeni metodlar tanımlanabilir:
Super class’ta bulunan getBalance() methodu aracılığıyla balance değişkenini elde ederiz.
Alan ve metot görünürlüğü
Sınıflar ve sınıflarla ilişkili kısımlar görünürlük değiştiricilerine (visibility modifiers) sahiptir. Burada karşımıza üç kavram çıkar: Public, protected ve private.
Public: Herkes tarafından erişilebilir.
Protected: Paket içerisinden, sınıf içerisinden, subclass içerisinden erişilebilir.
Private: Sadece sınıf içerisinden erişebilir.
Java’da paket (package), ilgili sınıfları gruplamak için kullanılır. Bunu bir dosya dizinindeki bir klasör olarak düşünebilirsiniz. Ad çakışmalarını önlemek ve daha iyi korunabilir bir kod yazmak için paketler kullanılır.
Bir alt sınıf, protected bir üst sınıftaki alana erişebilir:
Yukarıdaki görselde BankAccount sınıfının üst sınıf, SavingsAccount sınıfının alt sınıf olduğunu hatırlayalım. SavingsAccount sınıfındaki addInterest metodundaki işlemlerde balance değişkeni kullanılıyor. BankAccount sınıfına ait protected balance değişkenine ulaşabiliyoruz.
Overriding ve inheritance
Bir yöntemi geçersiz kılmak (overriding), devralınan (inherited) bir yöntemi yeniden tanımlar. Miras alınan bir yöntem, alt sınıfın ihtiyaç duyduğu şeyi yapmazsa, alt sınıf onu yeniden tanımlayabilir. Bir yöntem, üst sınıfın tanımıyla aynı imzaya (metodların adı, parametreleri) ve dönüş türüne sahipse, devralınan yöntemi geçersiz kılar.
Aşağıdaki örnekte override edilen üst sınıfın metodunu çağırmak için super kullanılması görülebilmektedir. 1000 birim para üzerindeki para çekme işlemleri için 10 birimlik bir ücret çıkarır. Withdraw fonksiyonunu geçersiz kılınmasını ve uygun olan bir fonksiyon yazılmasını gösterir.
Object sınıfından miras alınan metodlar override edilmelidir. Object sınıfından miras alınan metodlar belirli alt sınıflar için düzgün çalışmaz, bu sebeple bu metotların geçersiz kılınması gerekir (override).
Is-A ilişkisi
Java’da kalıtım bir is-a ilişkisidir. Yani, kalıtımı yalnızca iki sınıf arasında bir is-a ilişkisi varsa kullanabiliriz. Örneğin;
Burada araba araçtan, portakal meyveden miras alabilir.
Neden inheritance kullanıyoruz?
Inheritance kullanımı kodun yeniden kullanılabilirliğini (reusability) sağlar. Üst sınıfta bulunan kod, doğrudan alt sınıf tarafından kullanılabilir. Metot overriding, çalışma zamanı polimorfizmi(run time polymorphism) olarak da bilinir. Bu sebeple, Java’da kalıtım yardımıyla, polimorfizm elde edebiliriz.
Inheritance türleri
Beş tür kalıtım bulunmaktadır: Single inheritance, multilevel inheritance, hierarchical inheritance, multiple inheritance, hybrid inheritance.
Bu yazımızda, inheritance’ın ne amaçla kullanıldığını ve türlerini inceleyip, superclass-subclass ilişkisine değinerek inheritance’ın nesneye yönelik programlama için önemini vurguladık. Abstract ve interface ile ilgili yazımıza da göz atmayı unutmayın.
Kaynaklar