Bu bölümde nesne yönelimli programlama paradigmasının önemli konseptlerinden biri olan “Polimorfizm” konusu detaylı bir şekilde ele alacağız, genelden özele indirgeme yaparak ilk önce biyolojik polimorfizmi daha sonrasında nesne yönelimli programlamada polimorfizmi öğreneceğiz.
Nesne yönelimli programlama paradigması, sadece bir programlama yaklaşımı değil bir akıl yürütme biçimidir, düşünme modelidir. Bu paradigmanın özelliklerini geliştirenler evrenden ve insanın düşünüş tarzından büyük ölçüde alıntı yapmışlardır, bu paradigmanın özelliklerini bilenler nesne yönelimli programlama bilirler, ancak bu alıntıları bilenler nesne yönelimli düşünürler.
Polimorphism nedir ?
Polimorphism (polimorfizm), bir türün iki ya da daha fazla farklı formunun bir popülasyon içinde bulunması olarak tanımlanır. Bu durum, bir gende birden fazla özellik olmasından kaynaklanır.
Örneğin, jaguarları düşünelim. Bu türün deri renk bilgisini taşıyan genlerinde birden fazla renk bilgisi bulunur ve bunun bir sonucu olarak farklı morflarda (-renklerde) jaguarlar bulunmaktadır.

“Poly” ve “Morph” Kavramları
Poly kavramı, tahmin edebileceğimiz üzere “birden fazla”, “çok” anlamına gelmektedir ve bir kelimenin başına geldiğinde ona çokluk anlamı kazandırır. Jaguar örneğimizdeki polymorphism’den bahsedecek olursak baştaki “poly”, gen içerisindeki birden fazla sayıda olan renk özelliklerine atfedilmiştir.
Morph (morf) ise bir canlının hayattaki farklı aşamaları ve formlarıdır. Jaguar örneğimizde farklı renklerdeki jaguarlar, bu türün farklı morflarına (biçimlerine) örnektir. Polymorphic (polymorfik) sözcüğü ise basit olarak, bir tür içerisinde belirli bir özelliğin birden fazla morfunun (şeklinin, biçiminin..) olmasıdır.
Evrimsel Polimorfizm Teorisi
Evrimsel polimorfizm, bir türün farklı bireyleri arasındaki özellik farklılıklarının nedenini açıklamaya çalışan bir teoridir. Bu teori, evrim teorisinin mekanizmalarından biridir. Evrim teorisi, canlıların kendilerinden önce yaşamış olan atalarının bir versiyonu olduğunu söyler. Bu nedenle, polimorfizm teorisi, canlıların değişimlerinde ve evriminde önemli bir rol oynar.
Nesne yönelimli programlamada, farklı sınıfların birbirleriyle ilişkisi önemlidir. Polimorfizm, bu ilişkilerin anlaşılmasını sağlar. Bir referansın farklı biçimleri arasında belirli bir eylemin neden farklı olduğunu anlamak için polymorfizm bilmemiz gerekir. Bu, farklı sınıflar arasındaki kalıtım (inheritance) ilişkileri ile gerçekleşir. Yeni sınıflar, var olan sınıfların özel bir versiyonudur ve nesne yönelimli programlama dillerinin tarihi gelişimi sürecinde ortaya çıkmışlardır.
Sonuç olarak, nesne yönelimli programlama insan ve evrenin temellerine dayanan bir yaklaşımdır. Teknik detayların ötesinde, nesne yönelimli programlama, insanların doğal olarak düşünme ve problem çözme yeteneklerine uygun bir şekilde tasarlanmıştır. Bu yaklaşım, farklı sınıflar arasındaki ilişkilerin anlaşılmasını kolaylaştırarak, programlamada daha esnek ve güçlü bir yapı oluşturur.
Biyolojik Polimorfizm Bilmenin Nesne Yönelimli Düşünmede Avantajları
(%100 nesne yönelimli bir dil olan Java programlama dili baz alınmıştır)
Java programlama dilinde her sınıfın Object sınıfından türediği bir hiyerarşi vardır. Bu hiyerarşide, Object sınıfı en tepede yer alır ve diğer sınıflar bu sınıftan türemiştir. Bu durumda, her sınıf aslında Object sınıfının özel bir versiyonudur ve bir türdür. Biyolojik bir analogi kullanırsak, Object sınıfı bir türdür ve diğer sınıflar da farklı morflarını (biçimlerini) temsil eder. Ancak, her sınıf ortak bir atadan türediği için, Object sınıfının özelliklerini içerirler ve kendilerine özgü özellikleri de mevcuttur. Sınıf hiyerarşisini gösteren aşağıdaki görsel, bu yapıyı açıkça göstermektedir.

Görüldüğü üzere hiyerarşinin en tepesinde “Object” sınıfı vardır ve diğer sınıfılar’da ortak atanın özelliklerini kalıtıp, farklı morflar olarak oluşmaktadırlar, buradan çıkarılabilecek en temel sonuç Java programlama dilinde bütün sınıfların polimorfik olduğudur, çünkü hepsi aynı türün (programatik olarak “referans,”) farklı morflarıdır. (programatik olarak “örnek, instance”)
Hemen bir örnek ele alalım..

Dokümantasyonda yapılan notasyonları dikkate alıp kodumuzu yorumlayacak olursak;
1.Satırda ; A referansına, A örneği atanarak “object” nesnesi oluşturulmuş (Ortak atanın kendisi oluşturulmuş).
2.Satırda ; A referansına B örneği atanarak “object1” nesnesi oluşturulmuş (A ortak atasının, B morfu {biçimi} oluşturulmuş)
3.Satırda ; A referansına C örneği atanarak “object2” nesnesi oluşturulmuş (A ortak atasının C morfu {biçimi} oluşturulmuş).
Görüldüğü üzere parantez içerisindeki altı çizili tanımlar daha derin anlamlar ifade etmektedir, ve ifade edilen bu anlamlar çok daha net ve anlaşılırdır.
Yani biyolojik polimorfizm bilmek bize
A a = new B() gibi bir tanımlama gördüğümüzde ;
"a nesnesi oluşturulmuş"Diye değil.
"A ortak atasından B morfu oluşturulmuş buna göre B morfu A türünün özel bir versiyonudur, hiyerarşinin üstünde A sınıfı, altında B sınıfı bulunur, dolayısıyla B sınıfı A sınıfının bütün fonksiyonelliklerini içerir, ve farklı bir morf olduğundan dolayı A sınıfında olmayan bazı fonksiyonellikleri de içerecektir".Şeklinde düşünme yetisi kazandırır.
Bir nesnenin oluşturulması, sadece bellekte yer ayrılması anlamına gelmez. Asıl önemli olan, bu nesnenin hangi sınıfın bir örneği olduğudur. Örneğin, "A a = new B()" ifadesinde, "new B()" ifadesi bellekte yeni bir B nesnesi oluşturur ve bunu "a" referansına atar. Bu durumda, "a" nesnesi "A" sınıfının bir örneği değil, "B" sınıfının bir örneğidir.
Burada önemli olan, "B" sınıfının "A" sınıfından türemiş bir sınıf olmasıdır. Yani "B" sınıfı, "A" sınıfının özelliklerini (değişkenler ve metotlar) miras almıştır. Dolayısıyla, "B" sınıfı, "A" sınıfının sahip olduğu bütün özellikleri içerir ve ayrıca kendi özellikleri de olabilir.
Bu durumda, "A a = new B()" ifadesinde, "a" referansı "B" sınıfının bir örneğine atandığı için, "a" referansı üzerinden hem "B" sınıfının özelliklerine hem de "A" sınıfının özelliklerine erişilebilir. "B" sınıfının özellikleri, "A" sınıfında olmayan bazı özellikleri içerebilir, çünkü "B" sınıfı, "A" sınıfının bir versiyonu veya özel bir türevidir.
Bu durum, Java programlama dilindeki sınıflar arasındaki ilişkileri ve kalıtım hiyerarşisini anlamamızı sağlar. Özellikle polimorfizm kavramı, bu ilişkilerin anlaşılmasını sağlar.
Şimdi daha teknik detaylarıyla nesne yönelimli programlamada polimorfizm konseptini ele alalım.
Nesne Yönelimli Programlamada Polimorfizm
Polimorfizm, bir nesnenin aynı referans üzerinden farklı formları almasıdır ve aldığı bu formlar sayesinde her nesnenin (aynı eylem için) farklı bir davranış sergilemesine olanak tanır, ne demek istiyoruz hemen örnekleyelim;

Sınıf diyagramını çizdiğimiz bu örneğimizde, ortak ata (parent class) vahşi kedidir. Evcil kedi, çin dağ kedisi ve van kedisi bu ortak atadan türemiştir (child class) dolayısıyla ortak atanın yapabileceği tüm eylemleri (koşmak, beslenmek, uyumak) gerçekleştirebileceklerdir fakat her sınıf bu eylemler için farklı özelliklere sahip olacaktır, daha da açmak gerekirse ;
4 Kedi türü de koşabilir (fakat farklı hızlarda) ,beslenebilir (fakat farklı şekillerde) , uyuyabilir (fakat farklı sürelerde).
Şimdi başta yaptığımız tanımı bu örneğe uyarlayalım
Polimorfizm, bir nesnenin (Kedinin) aynı referans (Vahşi Kedi) üzerinden farklı formları (Evcil kedi, Çin Dağ Kedisi, Van Kedisi) almasıdır ve aldığı bu formlar sayesinde her nesnenin (Kedinin) aynı eylem için (Koşmak, Beslenmek, Uyumak) farklı bir davranış sergilemesidir. (farklı hızlarda koşmaları, farklı şekillerde beslenmeleri, farklı sürelerde uyumaları).
Bu şemayla ilgili bir kod yazalım..
VahsiKedi kedi = new EvcilKedi()
VahsiKedi kedi2= new CinDagKedisi()
kedi.kos()
Çıktı = Evcil kedi 30km/s hızla koşuyor
kedi2.kos()
Çıktı = Çin dağ kedisi 65km/s hızla koşuyor
burada görüldüğü üzere, aynı referans (parent class reference) farklı morflara (child class object) refere edilerek tanımlamalar gerçekleşmiş ve aynı eylemler kedi ve kedi2 referansı farklı davranışlar sergilemişlerdir, bu aslında nesne yönelimli programlamada yaygın olarak kullanılan dinamik polimorfizm örneğidir.
Polimorfizmin yaygın olarak bilinen 2 türü vardır
- Dinamik Polimorfizm (Dynamic Polymorphism)
- Statik Polimorfizm (Static Polymorphism)
Dinamik Polimorfizm (Dynamic Polymorphism)
Dinamik polimorfizm, nesne yönelimli programlamanın temel prensiplerinden biridir ve bir fonksiyonu farklı alt sınıflardaki nesneler üzerinde farklı şekillerde çalışacak şekilde kullanma yeteneğini ifade eder (bir önceki yaptığımız kedi örneği dinamik polimorfizm örneğidir). Bu, programcılara daha esnek ve verimli kod yazma imkanı sağlar.
Dinamik polimorfizm,
Run-Time polymorphism, Dynamic binding, Run-Time binding, Late binding ve Method overriding olarak da bilinir, ve neden bu şekillerde bilindiğini de bilmemiz gerekir bunun nedenini öğrenmemiz için Run time ve Compile time denen 2 terimi bilmemiz gerekir hemen bunlara göz atalım ;
Çalışma Zamanı ve Derleme Zamanı (Run Time and Compile Time)

Compile Time
Derleme zamanı (Compile time) herhangi bir programlama dilinin kaynak kodlarının Assembly programlama diline dönüştürülmesine kadar geçen zamandır.
Run-Time
Çalışma zamanı (Run-time) herhangi bir programlama dilinde yazdığımız kaynak kodlarının Assembly kodundan işlemcinin anlayabileceği binary yapısına (machine code) dönüştürülmesine kadar geçen zamandır diğer bir ifadeyle programın çalışması sırasında geçen zamandır.
Şimdi dinamik polimorfizme tekrar dönelim, ve yarım kalan sorularımızı cevaplayalım
Temel olarak dinamik polimorfizm, method overriding yöntemi kullanılarak gerçekleştirilir, bir sınıf hiyerarşisinde farklı sınıflar içerisinde aynı metodun bulunması, hangi sınıf içerisinden hangi metodun çağrılacağının karar verilmesini gerektirir, ve bu karar programın çalışması sırasında verilir, dolayısıyla bu yüzden run-time polymorfizm olarak da bilinir, derleme zamanında değil de çalışma zamanında bu kararın verilmesi nedeniyle de late binding (geç bağlama) denmiştir. binding kelimesi ise hangi metotodun hangi sınıf ile ilişkili olduğuna atıfta bulunmak için kullanılır.
Dinamik polimorfizm ile ilgili diyagramını çizdiğimiz kedi örneğini ele alalım ;
class WildCat {
public void sleep(){
System.out.println("Wild cat will sleep for 3 hours");
}
}
class DomesticCat extends WildCat {
@Override
public void sleep() {
System.out.println("Domestic cat will sleep for 8 hours");
}
}
class MountainCat extends WildCat{
@Override
public void sleep() {
System.out.println("Mountain cat will sleep for 5 hours");
}
}
public class CatTestClass {
public static void main(String[] args) {
//Wild cat reference and object.
WildCat cat = new WildCat();
//Wild cat reference and domestic cat object
WildCat cat1 = new DomesticCat();
//Wild cat reference and mountain cat object
WildCat cat2 = new MountainCat();
cat.sleep();
cat1.sleep();
cat2.sleep();
}
}
Output :
Wild cat will sleep for 3 hours
Domestic cat will sleep for 8 hours
Mountain cat will sleep for 5 hours
Burada hangi sınıf içerisindeki hangi metodun çağırılacağı run-time aşamasında belirlenir (yani program çalışırken) dikkat edilmesi gereken bir diğer nokta ise alt ataların (child classes) override ettiği metotların onlardan new keywordu ile bir nesne insantiate edildiğinde ilk olarak çalıştığıdır, eğer ki bu metot override edilmeseydi ortak atada (parent class) olan metodun bizzat kendisi faaliyet gösterecekti ne demek istiyoruz ?
class WildCat {
public void sleep(){
System.out.println("Wild cat will sleep for 3 hours");
}
}
class DomesticCat extends WildCat {
//No override
}
public class CatTestClass {
public static void main(String[] args) {
//Wild cat reference and Domestic cat object.
WildCat cat = new DomesticCat();
cat.sleep();
}
}
Output :
Wild cat will sleep for 3 hours
Görüldüğü üzere ortak atada (parent class) olan metot faaliyet gösterdi fakat override işlemi yapılsaydı öncelik alt atanın override ettiği metotta olacaktı.
Static Polymorphism (Statik Polimorfizm)

Çoğumuzun bu tarz fonksiyon soruları ile en az bir kere karşılaştığına eminim 🙂
Yukarıdaki basit fonksiyon sorusunu düşünelim, 3 tane fonksiyon var f(x) fonksiyonu tek parametre alıyor (x), f(x,y) fonksiyonu iki parametre alıyor (x ve y) ve ardından f(x,y,z) fonksiyonu ise 3 parametre alıyor biz bu soruyu çözerken hangi fonksiyonu ne zaman kullanacağımıza nasıl karar veriyoruz ? tabi ki parametre sayısına göre, eğer ki tek parametre varsa f(x) 2 tane varsa f(x,y) 3 tane varsa f(x,y,z) fonksiyonlarında değerleri yerine koyup sonuca ulaşıyoruz.
Peki bu örneği kodlayacak olsaydık nasıl kodlardık ;
public class Main{
public static void main(String[] args) {
float result = ( f(3)+f(4,5) ) / f(3,6,9);
System.out.println(result);
}
public static float f(int x){
return 2*x;
}
public static float f(int x, int y){
return (float) (Math.sqrt(x) +y);
}
public static float f(int x, int y, int z){
return (x+y+z)/3;
}
}
Output :
2.1666667 (13/6)
Tıpkı bizim düşündüğümüz gibi compiler da parametre sayılarını düşünüyor ve buna göre hangi fonksiyonu kullanacağına karar veriyor.
Aynı fonksiyon ismini kullanarak farklı formlarda fonksiyonlar yazabiliriz, ve bu fonksiyonları kullanacak kişinin hangi durumda hangi formdaki fonksiyonu kullanacağını ise parametre sayısı, parametre tipi gibi faktörler belirler. Programlamada bu işleme metot aşırı yükleme (Method Overloading) denir, “aşırı yükleme (overloading)” denmesinin sebebi bir fonksiyona birden fazla form (morf) yüklenmesidir.
Şimdi daha teknik detaylarıyla static polymorphism konseptini ele alalım,
Statik polimorfizm, Compile-Time polymorphism, Static binding, Compile-Time binding, Early binding ve Method overloading olarak da bilinir peki neden böyle bilinir ?
Compile-Time polymorphism olarak da adlandırılır çünkü, statik polimorfizm temel olarak metot aşırı yükleme (method overloading) işlemi ile gerçekleştirilir ve metot aşırı yükleme işleminde hangi metodun hangi form için çalışacağının kararı compile time aşamasında verilir. diğer yandan Early Binding (Erken bağlama) denmesinin sebebi program çalışmadan önce bu kararın verilmesidir, yani hangi metot formunun çalışacağı daha program çalışmadan kesinleşmiştir ve bu yüzden erken bağlama denir.
Statik polimorfizm, dinamik polimorfizm’de olduğu gibi birden fazla sınıf ile ilişkili değildir, statik polymorfizmde bir sınıf içerisinde sınıfın herhangi bir davranışı veya herhangi bir metodun aynı metot ismiyle farklı şekillerde (biçimlerde) aynı metot içerisinde tanımlanmasıdır, yani temel olarak statik polimorfizm metot overloading işlemidir.
Burada konumuzun sonuna geldik, okuyup zamanını veren herkese teşekkür ederim. Bu konuyla ilgili herhangi bir sorunuz varsa yorum yapmaktan çekinmeyin sorunuzun kısa sürede cevaplanacağına emin olabilirsiniz, konuyu pekiştirmek ve öğrendiklerimizi kısaca bir toplamak amacıyla oluşturduğum video aşağıda yer almaktadır, izlemenizi şiddetle tavsiye ederim bir sonraki konuda görüşmek üzere 🙂