Inheritance Nedir ? Extends&Super Kavramları ve Method Overriding

Fatih İzgi
Kodcular
Published in
6 min readAug 17, 2021

Bir önceki yazımızda Encapsulation konusunu ele almıştık ve böylece sınıflarımızı oluştururken nelere dikkat etmemiz gerektiğini ve nesnelerimizi nasıl koruyabileceğimizi öğrenmiştik. Bu yazımızda ise Inheritance konusunu ele alacağız. Inheritance kelimesi dilimizde Kalıtım, Miras Alma gibi anlamlara gelmektedir.

İlk yazımızda OOP kavramının “Gerçek hayatın yazılıma uyarlanması” olduğunu söylemiştik. Bu sebeple ufak bir gerçek hayat örneği vererek mantığı anlamaya çalışalım :

Teknolojik ürünler satan bir mağazaya girdiniz. Bilgisayar, telefon, şarj aleti, kulaklık gibi pek çok farklı kategori görmeniz mümkün. Bu ürünlerin hepsinin ortak bazı özellikleri bulunmaktadır. Örneğin her ürün fiyat bilgisi, garanti süresi gibi özelliklere sahiptir. Bununla beraber, her ürünün pek çok farklı özelliği de bulunmaktadır. Örneğin işletim sistemi veya kablo uzunluğu gibi özellikler her teknolojik alette bulunmak zorunda değildir. Tüm ürünlerde ortak özelliklerin bulunmasını aslında bir miras veya kalıtım olarak yorumlayabiliriz. Bilgisayar, telefon, şarj aleti ve kulaklık kategorisindeki ürünlerin hepsi esasında “Teknolojik Ürün” başlığı altındadır ve buradan türeyerek farklılaşmışlardır. Yazılım dünyasında da mantık aynen bu şekildedir. Ne demek istediğimizi daha ayrıntılı incelemek için önceki örneğimiz üzerinden devam edelim. Hem gelen güncellemeleri görmek hem de önceki yazıları okumayanlar için problem özeti :

Projede, bir sosyal medya uygulamasındaki kullanıcının profil sayfası üzerine kullanıcı bilgilerini yerleştirmek istiyoruz (Şimdilik bu işlemleri sadece ekrana bastırma olarak gerçekleştireceğiz. Ancak gerçekten bir web sitesi veya mobil uygulama geliştirirken de nesne kullanma mantığı örnekteki gibi olacaktır. Veri tabanından nesne verileri çekilir ve nesnelerin özellikleri ekrana yerleştirilir). Uygulamada kullanıcılar, reklam görmek istemeyerek yıllık para ödeyebilir veya uygulamayı ücretsiz kullanmaya devam ederek reklam izlemeyi tercih edebilir. Bu sebeple “Premium Kullanıcı” ve “Normal Kullanıcı” olmak üzere iki farklı kullanıcı tipi bulunsun. Her iki kullanıcı türü de kullanıcı adı, yaşadığı ülke ve yaş özelliklerine sahip olsun. Ayrıca, tüm kullanıcılar kendi özelliklerini gösterebileceği bir davranışa (showUserInfo() metoduna) sahip olsun. Premium kullanıcılar ek olarak yıllık ödenen para özelliğine ve kupon koduna göre değişen fiyat miktarını hesaplayan bir davranışa (calculatePriceAfterCoupon() metoduna) sahip olsun.

INHERITANCE

Bir sınıfın sahip olduğu özellik ve metotların başka sınıflara aktarılmasıdır. Özelliklerini aktaran sınıf Ata ve özellikleri miras alan sınıf ise Çocuk olarak düşünülebilir. Yazılımda Ata(Parent), yani özelliklerini aktaran sınıf Super Class veya Base Class olarak adlandırılır. Bununla beraber Çocuk(Child), yani özellikleri miras alan sınıf Sub-Classs veya Derived Class olarak adlandırılır. Çocuk sınıfın, Ata sınıf özelliklerini miras alabilmesi için de “extends” anahtarı kullanılır. Hemen örnekler üzerinden ilerleyerek konuyu öğrenmeye başlayalım :

Öncelikle bir adet Ata sınıfa ihtiyacımız var :

Şimdi bir Çocuk sınıf tasarlayalım :

Görüldüğü gibi, Çocuk sınıfın Ata sınıfa ulaşması için “extends” anahtarını kullandık. Bu sayede özellik ve metotların miras alınacağını bildirmiş olduk ancak bu noktada Java hata verecektir. Bu hatayı gidermek için Çocuk sınıfta da bir Constructor oluşturmamız gerekir.

Constructor oluşturmamıza rağmen Java hata vermeye devam edecektir. Bu hatayı gidermemiz için bu Constructor içerisinde üst sınıfın sahip olduğu Constructor metotu(aslında sahip olduklarından bir tanesini) çağırırız. Bunu super() metodu sayesinde gerçekleştiririz. Ayrıca Çocuk Constructor içerisinde de uygun parametrelerin(Ata Constructor’da bulunan parametrelerin) olması gerekmektedir. Örneğin :

Dilerseniz ilerlemeden önce super() sayesinde bir üst sınıftaki Constructor’u çağırdığımızı ispatlayalım :

Output :

Ana sınıfta bulunan Constructor çalıştırıldı.

Çocuk sınıfta bulunan Constructor çalıştırıldı.

Gördüğünüz gibi Main içerisinde yalnızca PremiumUser sınıfından bir nesne oluşturduk. Buna rağmen hem User sınıfının Constructor’ı içerisinde bulunan işlemler hem de PremiumUser sınıfının Constructor’ı içerisinde bulunan işlemler gerçekleştirildi. Bu durum ise super() metodu sayesinde gerçekleştirildi.

Üzerinde düşünelim : Ata sınıfın birden fazla Constructor’a sahip olduğu bir durumda, Çocuk Constructor içerisinde (super() metotu ile), Ata sınıfa ait Constructor’lardan kaç tanesini çağırabiliriz ? Bu sorunun cevabı yalnızca 1'dir. this() kullanımından hatırlayacağınız üzere Java, bu tarz ifadeleri Constructor’un en üstünde görmek istiyor. Eğer iki farklı satırda super() metodu ile iki farklı Constructor çağırmaya çalışırsak Java hata verecektir ve size ikinci super() metotu en üste taşımanız gerektiğini söyleyecektir. Bunu yapmanız taktirde ilk super() aşağı düştüğünden dolayı yine hata alacaksınız. Örneğin :

Gördüğünüz gibi User sınıfı içerisinde iki farklı Constructor bulunuyor. Bu Constructorları ayrı ayrı PremiumUser Constructor’u içerisinde çağırmaya çalıştığımızda Java bizi uyarıyor (call to super must be first statement in constructor). Bu şekilde bir işlem gerçekleştirmemiz ise mümkün olmuyor.

Peki alt sınıf, üst sınıfın sahip olduğu tüm özellikleri miras almak zorunda mı ?

Elbette hayır. Yukarıda bahsettiğimiz gibi birden fazla Constrcutor oluşturarak hangi özellikleri miras bırakacağımızı belirleyebiliriz. Alt sınıf, ihtiyacı olan Constructor’ı kullanarak sadece belirli özellikleri miras alabilir. Örneğin :

Görüldüğü gibi üst sınıfta hem name özelliğine değer atayan bir Constructor hem de age özelliğine değer atayan bir Constructor (ayrı ayrı) bulunuyor. Alt sınıf ise sadece name özelliğini miras almayı tercih edebildi.

Peki alt sınıf, ekstradan özelliklere sahip olabilir mi ?

Elbette olabilir. Yukarıdan miras almak istediğimiz özellikleri miras aldıktan sonra istediğimiz özellikleri tanımlayabiliriz. Örneğin :

Gördüğünüz gibi üst sınıfta tanımlanan özellikleri miras olarak aldık, üstüne de PremiumUser sınıfına özel bir country değişkeni tanımladık. (Not : Alt sınıf aynı şekilde ekstra davranışlara, yani metotlara da sahip olabilir.)

Şu ana kadar öğrendiklerimizi incelediğinizde bir nokta dikkatinizi çekecektir : Üst sınıfın sahip olduğu name ve age değişkenlerine alt sınıftan super() metotu sayesinde dolaylı olarak erişebildik ancak doğrudan PremiumUser sınıfı içerisinde name ve age isimli iki değişken tanımlamadık. Dolayısıyla bu özelliklere ait getter/setter metotları(Klasik, şu ana kadar gördüğümüz şekilde) tanımladığımızda Java hata verecektir.

Peki, alt sınıfın miras aldığı özelliklerine nasıl erişeceğiz ?

Üst sınıfın sahip olduğu getter/setter metotlarını alt sınıf içerisinde de tanımlamamız gerekiyor ancak bu sefer farklı bir getter/setter tasarımı ile karşılaşıyoruz. Miras alınan özellikler, alt sınıf içerisinde doğrudan tanımlanmadığı için üst sınıf metotlarına erişmek için super anahtar kelimesini kullanmamız gerekiyor. Örneğin :

Gördüğünüz gibi PremiumUser, yani alt sınıf içerisinde oluşturduğumuz getter/setter metotlar içerisinde, üst sınıfın sahip olduğu getter/setter metotları çağırdık. Bu çağırma işlemini ise super anahtarı sayesinde gerçekleştirebildik. Burada getter/setter metotlar üzerinde yazan @Override yazısı (Annotation) dikkatinizi çekmiş olabilir. Bu kavram Inheritance içerisinde gördüğümüz en önemli kavramlardan bir tanesidir. Üst sınıfa ait olan metotların alt sınıftan miras alınma işlemidir.

METHOD OVERRIDING

Üst sınıfa ait özelliklerin alt sınıflar tarafından nasıl miras alındığını öğrendik. Aynı şekilde alt sınıf, üst sınıfta bulunan metotları da miras almak, kendi içerisinde kullanmak isteyebilir. İşte bu işleme Method Overriding adı verilir. Bu kavram dilimizde Ezme, Geçersiz Kılma gibi anlamlara gelmektedir. Bu şekilde isimlendirilmesinin sebebi ise alt sınıfın, üst sınıftan metot miras alması durumunda üst sınıftaki metodun geçerliliğini kaybetmesidir. Yani alt sınıf miras aldığı metot ile birlikte, üst sınıfın sahip olduğu metodu ezer, onun yerine geçmiş olur. Bu kavramı daha iyi anlamak için şu şekilde de izah edebiliriz : Alt sınıfın, üst sınıftaki metot ile aynı isimde bir metot tanımlayarak, kendi ihtiyaçları ve istekleri doğrultusunda bu metoda işlevler yüklemesidir, yani bir metodun yeniden yazılmasıdır. Yine de Method Overriding kavramının yalnızca açıklamalar ile kafada canlanması biraz zor olabilir. O yüzden hemen bir örnek üzerinden incelemeye başlayalım :

Output :

Premium User sınıfına ait Method çalıştırıldı.

Görüldüğü gibi ilk olarak üst sınıfta Method ismi ile tanımlanan metot alt sınıfta override edildi. Yani alt sınıfta aynı isimde bir metot daha tanımlandı. Alt sınıf bu metotu değiştirerek farklı bir şekilde kullanım gerçekleştirdi.

Elbette biz alt sınıfın bir metodu miras alması ile o metot özelliklerini de kullanmak isteyebiliriz. Bunun için yine super anahtar kelimesinden yararlanırız. super anahtarının metot içerisinde kullanımı ile üst sınıftaki metot içerisinde işlemler alt sınıfta da aynen gerçekleştirilmiş olur. Örneğin :

Output :

User sınıfına ait Method çalıştırıldı.

Premium User sınıfına ait Method çalıştırıldı.

Görüldüğü gibi super anahtarını kullandığımızda, bir üst sınıftaki metot içerisinde bulunan işlemler alt sınıftaki metot içerisinde de aynen gerçekleştiriliyor.

Avantaj : Üst sınıfta kullanılan bir metot zaten bulunuyor. Aynı metodu alt sınıfta kullanmak istediğimizde metot içerisini tekrar yazmamıza gerek kalmıyor. Böylece daha düzenli bir kod görüntüsü elde etmiş oluyoruz. Ayrıca, Method Overriding ekstra işlemler gerçekleştirmemize de olanak sağlıyor.

Dikkat Edilmesi Gerekenler :

1- Eğer üst sınıfta bulunan metot static, final veya private olarak tanımlanmışsa, bu metot Override edilemez. Bu noktada Java, hata verecektir.

2- Alt sınıfta bulunan metot ismi ile üst sınıfta bulunan metot ismi birebir aynı olmalıdır. Aksi taktirde bu bir Overriding işlemi olmaz.

3- Bir metot Override edilirken, metodun sahip olduğu özelliklerini değiştiremeyiz. Yani metot parametreleri (sayısı ve sırası) üst sınıfta nasıl ise alt sınıfta da öyle olmalıdır. Ayrıca üst sınıfta bir dönüş tipine sahip ise alt sınıfta da aynı dönüş tipine sahip olmalıdır.

PROJE

Inheritance konusunun ayrıntılarını ve inceliklerini öğrendiğimize göre kendi projemizin tamamını incelemeye başlayabiliriz :

Output :

İndirim sonucu ödenecek olan ücret : 344.99TL

Kullanıcı Adı : Kullanıcı245

Kullanıcı Ülke : Türkiye

Kullanıcı Yaşı : 23

Yıllık Ödenen Ücret : 599.99TL

Kullanıcı Adı : User245

Kullanıcı Ülke : England

Kullanıcı Yaşı : 22

--

--