Polymorphism, Türkçe karşılığı ile çok biçimlilik, object oriented programming (nesne tabanlı programlama) dillerinin ana yapısıdır. Kotlin programlama dilinde ise bu yapıyı nesnelerin farklı nesneler şeklinde davranmasını sağlar. Örnek ile bunu daha da iyi açıklamaya çalışalım.

Kotlin-Polymorphism

Örneğin Asker sınıfımız olsun, Çavuş Teğmen ve Yüzbaşı bu sınıftan miras alınsın (Inherit) şimdi bu güzel örneğe uygun olarak Polymorphism uygulamasını Kotlin dilinde yapalım.

open class Asker
{
    open fun selamVer()
    {
        println("Asker selam verdi!!")
    }
}

class Cavus : Asker()
{
    override fun selamVer()
    {
        println("Çavuş selam verdi!!")
    }
}

class Tegmen : Asker()
{
    override fun selamVer()
    {
        println("Teğmen selam verdi!!")
    }
}


class Yuzbasi : Asker()
{
    override fun selamVer()
    {
        println("Yüzbaşı selam verdi!!")
    }
}

fun main(args:Array<String>)
{
    var asker : Asker = Asker()
    var cavus : Cavus = Cavus()
    var tegmen : Tegmen = Tegmen()
    var yuzbasi : Yuzbasi = Yuzbasi()
    
    hazirOl(asker)
    hazirOl(cavus)
    hazirOl(tegmen)
    hazirOl(yuzbasi)
}

fun hazirOl(asker:Asker)
{
    asker.selamVer()
}


Burada Asker sınıfımızı oluşturduk ve Cavus, Tegmen ve Yuzbasi sınıflarımızı da Asker sınıfından türettik. Oluşturduğumuz Cavus, Tegmen ve Yuzbasi sınıflarında Asker sınıfında tanımlanan selamVer() metodunu her biri için ayrı ayrı override ettik. Daha sonra main metodunda nesnelerimizi tanımladık. Tanımlanan bu nesneleri hazirOl() metodunun parametresine gönderdik. Dikkat edilirse, hazirOl() metodunun parametresi Asker sınıfından miras alınmakta, dolayısıyla o sınıfın bir nesnesi olmak durumunda. Gönderdiğmiz parametreler ise zaten Asker sınıfından türetilmişti, dolayısıyla metodumuz Asker sınıfından türetilen nesnelere göre işlem yapmakta. İşte oluşturduğumuz bu yapıya “Polymorphism” adını veriyoruz. Ayrıca uygulamamız aşağıdaki çıktıyı verecektir.

Asker selam verdi!!
Çavuş selam verdi!!
Teğmen selam verdi!!
Yüzbaşı selam verdi!!

Peki, örneğin Asker sınıfından miras aldığım sınıfta “Cavus” sınıfı için metodu override etmeseydim ne olurdu?

open class Asker
{
    open fun selamVer()
    {
        println("Asker selam verdi!!")
    }
}

class Cavus : Asker()
{
  
}

class Tegmen : Asker()
{
    override fun selamVer()
    {
        println("Teğmen selam verdi!!")
    }
}



class Yuzbasi : Asker()
{
    override fun selamVer()
    {
        println("Yüzbaşı selam verdi!!")
    }
}

fun main(args:Array<String>)
{
    var asker : Asker = Asker()
    var cavus : Cavus = Cavus()
    var tegmen : Tegmen = Tegmen()
    var yuzbasi : Yuzbasi = Yuzbasi()
    
    hazirOl(asker)
    hazirOl(cavus)
    hazirOl(tegmen)
    hazirOl(yuzbasi)
}

fun hazirOl(asker:Asker)
{
    asker.selamVer()
}


Cavus sınıfı içindeki override edilen metodu kaldırdım, peki bu durumda uygulamam nasıl bir çıktı verecek? Dikkat edilirse, Cavus sınıfım zaten Asker sınıfından miras alınmıştı, selamVer() metodunu Cavus nesnesi için çağırdığımda, Cavus sınıfını miras aldığım üst sınıf olan (super) metottaki selamVer() metodunu çağıracaktır. Dolayısıyla yukarıdaki koda göre uygulamam bu kez aşağıdaki çıktıyı verir.

Asker selam verdi!!
Asker selam verdi!!
Teğmen selam verdi!!
Yüzbaşı selam verdi!!

Ayrıca, burada özel tipten genel tipe dönüşüm yaptık. İşte bu işleme de “upcasting” adını veriyoruz.


Nedir Bu Late Binding?

Şuana kadar ki yaptığımız örneklerde derleme anında karar verme ile örneklerimizi gerçekleştirdik. Yani, yukarıdaki hazirOl() metoduna gönderdiğimiz parametrelerin ne olduğu derleme anında belliydi. Burada, bu durum her zaman belli olmayabilir, işte buna Late Binding diyoruz. 

open class Araba()
{
    open fun secilenAraba()
    {
        println("Araba seçildi..")
    }
}

class Audi : Araba()
{
    override fun secilenAraba()
    {
        println("Audi seçildi..")
    }
}

class Mercedes : Araba()
{
    override fun secilenAraba()
    {
        println("Mercedes seçildi")
    }
}

fun main(args: Array<String>)
{
    var arabalar = Array<Araba>(3){Araba()}
    
    for(i in 0 until arabalar.size-1)
    {
        arabalar[i] = rastgeleSec()
    }
    
    for (araba in arabalar)
    {
        araba.secilenAraba()
    }
}

fun rastgeleSec() : Araba
{
    var randomSayi = (Math.random()*3).toInt()
    var mevcutAraba : Araba = Araba()
    
    when(randomSayi)
    {
        0 -> mevcutAraba = Araba()
        1 -> mevcutAraba = Audi()
        2 -> mevcutAraba = Mercedes()
    }
    
    return mevcutAraba
    
}


Yukarıda tanımını verdiğim “late binding” kavramını gerçekleştirdik. Dikkat edilirse gönderilen parametreler derleme zamanında belli değil, random sınıfından ürettiğimiz değer mevcutArabayı döndürüyor. Daha iyi anlamak için kodu kopyalayıp yapıştırın art arda üç kez çalıştırın, aynı değerlerin gelmediğini göreceksiniz. Çok soyut örnekler ancak bu kavramların tam oturabilmesi için Class, Object, Inherit kavramlarının oldukça iyi bilinmesi gerekiyor.