Tek Referans, Çok Tür
Polimorfizm, kalıtımın ve arayüzlerin getirisidir. Bir üst tür (veya bir arayüz) olarak tanımlanmış tek bir değişkenin herhangi bir alt türden nesneyi tutabilmesi ve üzerinde bir metot çağırdığınızda Java'nın değişkenin bildirilen türünün ima ettiği sürümü değil, nesnenin gerçek sınıfına ait sürümü çalıştırması anlamına gelir.
Kalıtım sayfasında temeli zaten görmüştünüz: bir alt sınıf, üst sınıfından bir metodu ezer. Polimorfizm, o ezmeyi değerli kılan şeydir - genel türe karşı kod yazmanıza ve her somut nesnenin kendi biçiminde davranmasına izin verir.
Hem a hem de b Animal olarak bildirilmiştir, ancak her biri kendi sesini yazdırır. Bu seçim, gerçek nesneye göre çalışma zamanında gerçekleşir.
Dinamik Metot Çağrımı
Bunun arkasındaki mekanizma dinamik metot çağrımıdır (dynamic method dispatch): ezilen bir örnek metodu için JVM, hangi gerçeklemenin çağrılacağına karar vermek üzere nesnenin çalışma zamanı sınıfına bakar. Derleyici yalnızca metodun bildirilen türde var olduğunu kontrol eder; asıl seçim, program çalışana kadar ertelenir.
Bir döngünün, her birinin ne olduğunu hiç sormadan tüm tür karışımıyla baş edebilmesini sağlayan şey budur:
Döngü yalnızca Shape'i bilir. Daha sonra bir Triangle extends Shape ekleyin, bu kod değişmeden çalışmaya devam eder - bütün mesele budur. Kod, somut tür listesine değil soyutlamaya bağlıdır.
Upcasting ve Downcasting
Bir Dog'u bir Animal değişkeninde saklamak upcasting'tir - hiyerarşide daha genel bir türe doğru yukarı çıkmak. Her zaman güvenlidir ve Java bunu örtük olarak yapar, çünkü her Dog bir Animal'dır (is a).
Ters yöne gitmek downcasting'tir - bir üst referansı alıp onu belirli bir alt tür gibi ele almak. Bu yalnızca nesne gerçekten o alt türse geçerlidir, bu yüzden cast'i açıkça yazmanız gerekir ve yanılırsanız bir ClassCastException riskine girersiniz:
Son cast sorunsuz derlenir - derleyici yanlış olduğunu kanıtlayamaz - ama çalışırken patlar çünkü bir Cat bir Dog değildir. Asla körü körüne downcast yapmayın.
Downcast'leri instanceof ile Koruyun
Downcasting yapmadan önce gerçek türü instanceof ile kontrol edin. Modern Java, sonucu aynı ifadede bağlamanıza izin verir (instanceof için desen eşleme), böylece ayrı cast'ten kurtulursunuz:
instanceof, null için false döndürür, bu yüzden bu kontrol sizi bir NullPointerException'dan da korur. Bununla birlikte, uzun instanceof zincirleri yazdığınızı fark ederseniz, bu genellikle davranışın ezilmiş bir metot olarak sınıfların içine ait olduğunun bir işaretidir - dallanmayı sizin için polimorfizm yapsın.
Overriding ve Overloading
Bu ikisi kulağa benzer gelir ama birbiriyle ilgisizdir ve onları karıştırmak klasik bir kafa karışıklığı kaynağıdır.
Overriding (ezme), bir alt sınıfın üst sınıf metodunu birebir aynı imzayla değiştirmesidir. Nesnenin türüne göre çalışma zamanında çözülür - kullanageldiğimiz polimorfizm budur.
Overloading (aşırı yükleme), tek bir sınıfın aynı ada sahip ancak parametre listeleri farklı birden çok metoda sahip olmasıdır. Argüman türlerine göre derleme zamanında çözülür ve hiçbir çalışma zamanı çağrımı içermez:
Derleyici, eşleşen describe'ı tamamen argümanın statik türünden seçer. Burada hiçbir üst/alt nesne yer almaz, dolayısıyla bu çalışma zamanı polimorfizmi değildir - yalnızca bir metot adını yeniden kullanır.
Sık Yapılan Bir Hata: Alanlar Polimorfik Değildir
Yalnızca örnek metotları çalışma zamanında çağrılır. Alanlar ve statik metotlar bildirilen türe göre çözülür ve bu birçok kişiyi yanıltır:
p.name(), Child'ın sürümünü çalıştırır (polimorfizm), ama p.label, Parent'ın alanını okur, çünkü alanlar ezilmez, gizlenir. Çözüm basittir: alanları private tutun ve onlara yalnızca metotlar üzerinden erişin, böylece polimorfik çağrı her zaman kazanır.
Sıradaki: Erişim Belirleyicileri
Polimorfizm yalnızca alt sınıflar doğru üyeleri görüp ezebildiğinde ve kodunuzun geri kalanı içeri uzanıp değişmezleri (invariant) bozamadığında temiz bir şekilde çalışır. Bu denge public, protected, private ve paket düzeyindeki erişimle - yani erişim belirleyicileriyle - kontrol edilir; sırada onlar var.
Sıkça Sorulan Sorular
Java'da polimorfizm nedir?
Polimorfizm, bir referans türünün birçok farklı sınıfa ait nesnelere işaret edebilmesi ve gerçekte çalışan metodun değişkenin bildirilen türüne göre değil, nesnenin çalışma zamanındaki gerçek türüne göre seçilmesi demektir. Yani bir Shape shape değişkeni bir Circle veya bir Square tutabilir ve shape.area() çağrısı doğru sürümü otomatik olarak çalıştırır.
Java'da overriding ile overloading arasındaki fark nedir?
Overriding (ezme), bir alt sınıfın üst sınıfın metodunu aynı imzayla değiştirmesidir - çalışma zamanı polimorfizmini sağlayan budur. Overloading (aşırı yükleme), tek bir sınıfın aynı ada sahip ancak parametre listeleri farklı birden çok metoda sahip olmasıdır; derleyici, argümanlara bakarak derleme zamanında birini seçer. Override, nesnenin türüne göre çalışma zamanında çözülür; overload ise argüman türlerine göre derleme zamanında çözülür.
Java'da upcasting ile downcasting arasındaki fark nedir?
Upcasting, bir alt nesneyi üst türü gibi ele alır (Animal a = new Dog();) - her zaman güvenlidir ve genellikle örtüktür. Downcasting ters yöne gider (Dog d = (Dog) a;) ve yalnızca nesne gerçekten o alt türse güvenlidir; aksi halde ClassCastException fırlatır. Her downcast'i önce instanceof ile koruyun.