Bölüm 4.4: Çok Biçimlilik
Giriş
Öğrenme Çıktıları
Çok biçimliliği (polymorphism) tanımlamak; derleme zamanı ve çalışma zamanı çok biçimliliğini ayırt etmek.
Üst sınıf referansına alt sınıf nesnesi atanabildiğini, tersinin neden derleme hatası verdiğini açıklamak.
Yukarı ve aşağı dönüşümü (upcasting, downcasting) yazmak; ClassCastException'ı instanceof ile önlemek.
Çağrılan metodun nesnenin gerçek sınıfına göre seçildiğini adım adım izlemek; metot aramasını hiyerarşide göstermek.
Overloading ile overriding'i karşılaştırmak; hangisinin derleyici, hangisinin JVM tarafından seçildiğini bilmek.
Şekil dizisi ve tanksavar oyunu örnekleriyle üst sınıf tipiyle genel kod yazmak; equals'ın simetri tuzağını tanımak.
Çok Biçimlilik Kavramı
Çok Biçimlilik (Polymorphism)
- Yunanca poly (çok) + morph (biçim): aynı ifadenin, farklı nesnelerde farklı davranışa yol açmasıdır.
- Bir referans, kendi tipindeki veya bir alt sınıfa ait bir nesneye atıfta bulunabilir.
hayvan.sesCikar()çağrısı köpek nesnesindeHav hav!, kedi nesnesindeMiyav!yazdırır.- Kodu üst sınıf tipiyle bir kez yazarız; her nesne çağrıya kendi sürümüyle cevap verir.
Çok Biçimlilik Kavramı
Çok Biçimliliğin İki Türü
| Derleme zamanı (statik) | Çalışma zamanı (dinamik) | |
|---|---|---|
| Mekanizma | Aşırı yükleme (overloading) | Metot geçersiz kılma (overriding) + dinamik bağlama |
| Seçimi yapan | Derleyici (javac) | JVM, program çalışırken |
| Neye bakılır? | Argümanların sayısı ve bildirilen tipleri | Nesnenin gerçek sınıfı |
| Kalıtım gerekir mi? | Hayır, tek sınıfta olur | Evet, sınıf hiyerarşisi gerekir |
Aynı ad, farklı parametreler
int topla(int a, int b) { ... } double topla(double a, double b) { ... } topla(5, 3); // int sürümü topla(3.5, 2.7); // double sürümü
Aynı imza, farklı sınıflar
Hayvan h1 = new Kopek(); Hayvan h2 = new Kedi(); h1.sesCikar(); // Hav hav! h2.sesCikar(); // Miyav!
"Çok biçimlilik" denince çoğunlukla çalışma zamanı çok biçimliliği kastedilir.
Çok Biçimlilik Kavramı
Örnek Sınıf Hiyerarşisi
class Hayvan { public void sesCikar() { System.out.println("Hayvan sesi"); } public void kos() { System.out.println("Hayvan koşuyor"); } public void uyu() { System.out.println("Hayvan uyuyor"); } } class Kopek extends Hayvan { @Override public void sesCikar() { System.out.println("Hav hav!"); } @Override public void kos() { System.out.println("Köpek koşuyor"); } public void havla() { System.out.println("Köpek havlıyor"); } } class Kedi extends Hayvan { @Override public void sesCikar() { System.out.println("Miyav!"); } public void tirmala() { System.out.println("Kedi tırmalıyor"); } }
Kopek,sesCikar()vekos()'u geçersiz kılar,havla()'yı ekler;KediyalnızcasesCikar()'ı geçersiz kılar,tirmala()'yı ekler.- Kopek de Kedi de bir Hayvan'dır (is-a); ikisi de
Hayvantipindeki bir değişkende tutulabilir.
Referanslar ve Tip Uyumu
Referans, Alt Sınıf Nesnesini Gösterebilir
// Köpek nesnesine referans Kopek kopek = new Kopek(); // Hayvan referansı Kopek'i gösterebilir Hayvan hayvan = kopek; // Object referansı da gösterebilir Object nesne = hayvan; System.out.println(kopek == hayvan); System.out.println(hayvan == nesne); System.out.println(nesne.getClass());
new Kopek() heap'te bir nesne oluşturur; kopek referansı onu gösterir.
Yeni nesne oluşmaz: hayvan, aynı nesneyi gösteren ikinci referanstır. Kopek bir Hayvan olduğu için atama geçerlidir.
Her sınıf Object'ten türediği için Object referansı da aynı nesneyi gösterebilir.
== referansları karşılaştırır: ikisi de aynı nesneyi gösterdiği için true.
Yine aynı nesne: true. Üç referans, tek nesne.
Referansın tipi Object olsa da nesnenin gerçek sınıfı değişmez: Kopek.
Referanslar ve Tip Uyumu
Referans Tipi ve Nesne Tipi
Hayvan h = new Kopek(); // bildirilen tür: Hayvan // gerçek tür: Kopek
| Soru | Kim karar verir? |
|---|---|
| Bu metot çağrılabilir mi? | Derleyici, referans tipine bakar |
| Hangi sürüm çalışır? | JVM, nesne tipine bakar |
| Hangi atamalar geçerli? | Derleyici, referans tipine bakar |
Hayvan hayvan = new Kopek(); hayvan.havla(); // derleme hatası
error: cannot find symbol symbol: method havla() location: variable hayvan of type Hayvan
Kumandada düğmesi olmayan özellik kullanılamaz: nesne bir köpek olsa da Hayvan "kumandasında" havla() düğmesi yoktur.
Referanslar ve Tip Uyumu
Referans Kuralı
- Bir nesnenin alt tipini, üst sınıf tipindeki bir referansa atayabiliriz.
- Ancak üst sınıfın nesnesini, alt sınıf tipindeki bir referansa atayamayız.
Geçerli
// Kopek, Hayvan'ın alt türüdür Hayvan hayvan = new Kopek(); Object nesne = new Kedi();
Her köpek bir hayvandır: hayvandan beklenen her şeyi köpek de yapabilir.
Hatalı
// Hayvan, Kopek'in üst sınıfıdır Kopek kopek = new Hayvan();
error: incompatible types: Hayvan cannot be converted to Kopek
Bir hayvan havla() bilmeyebilir; derleyici buna izin vermez.
Hayvan h = new Kopek(); Kopek k = h; de aynı hatayı verir. Derleyici h'nin gösterdiği nesneyi değil, bildirilen tipini (Hayvan) görür; çözüm açık dönüşümdür.Tip Dönüşümü
Yukarı ve Aşağı Dönüşüm
| Yukarı (upcasting) | Aşağı (downcasting) | |
|---|---|---|
| Yön | Alt → üst | Üst → alt |
| Yazım | Otomatik (örtük) | Açık: (Kopek) h |
| Güvenli mi? | Her zaman | Nesneye bağlı |
| Diğer adı | Genişletme (widening) | Daraltma (narrowing) |
Kopek kopek = new Kopek(); Hayvan h = kopek; // yukarı: otomatik Hayvan h1 = (Hayvan) kopek; // gereksiz
Hayvan h = new Kopek(); Kopek k = (Kopek) h; // aşağı: açık k.havla(); // Köpek havlıyor
Tip Dönüşümü
Aşağı Dönüşüm (Downcasting)
- Bir üst sınıf referansı, alt sınıf tipine açıkça (explicitly) dönüştürülebilir:
(AltSinif) referans. - Üst sınıf referansı, ancak bu dönüşümle bir alt sınıf değişkenine atanabilir.
- Amaç: üst sınıfta bulunmayan (yalnızca alt sınıfa ait) metotları çağırmak.
- Dönüşüm nesneyi değiştirmez; yalnızca derleyiciye "bu referansı Kopek olarak gör" der.
Hayvan h2 = new Kopek(); Kopek k = (Kopek) h2; k.havla(); ((Kopek) h2).havla(); // tek satırda
Köpek havlıyor Köpek havlıyor
Öncelik tuzağı
Nokta (.) dönüşümden önce uygulanır. Parantezler unutulursa (Kopek), havla() çağrısının sonucuna uygulanmak istenir:
(Kopek) h2.havla(); // hatalıerror: not a statement
String s = (Kopek) h2.isim();
// h2.isim() önce aranır → Hayvan'da yokerror: cannot find symbol symbol: method isim() location: variable h2 of type Hayvan
Doğrusu: ((Kopek) h2).havla();
Tip Dönüşümü
Hatalı Aşağı Dönüşüm: ClassCastException
Hayvan h4 = new Kedi(); Kopek k4 = (Kopek) h4; // derlenir! k4.havla();
Exception in thread "main" java.lang.ClassCastException: class Kedi cannot be cast to class Kopek
Kopek k5 = (Kopek) new Hayvan(); // derlenir!
java.lang.ClassCastException: class Hayvan cannot be cast to class Kopek
- Derleyici izin verir:
Hayvantipindeki bir referans pekâlâ birKopek'i gösteriyor olabilir. - JVM, dönüşüm anında nesnenin gerçek sınıfını denetler: nesne bir
Kediya da düz birHayvanise program istisnayla durur. - Yani üst sınıf nesnesi, alt sınıf referansına hiçbir zaman atanamaz; dönüşüm yalnızca nesne gerçekten o alt sınıftansa başarılı olur.
B objB = (B) objA; derlenir, ama yalnızca objA gerçekten bir B nesnesini gösteriyorsa çalışır. Emin olunamıyorsa önce instanceof ile sınanır.İlgisiz tipler arasında dönüşümü derleyici hiç kabul etmez: (Kedi) kopek → incompatible types: Kopek cannot be converted to Kedi.
Tip Dönüşümü · Adım adım kod izleme
ClassCastException: Kod İzleme
Yukarı dönüşüm: Hayvan tipindeki h1 bir Kopek nesnesini gösterir.
h2 ise bir Kedi gösterir. Derleyici için ikisi de yalnızca Hayvan.
Derleyici dönüşüme izin verir. Çalışırken JVM nesneye bakar: gerçekten Kopek. k1 aynı nesneyi gösterir.
Kopek tipli referansla havla() çağrılabilir.
Bu satır da derlenir. Ama JVM, Kedi'den yukarı çıkan zincirde Kopek bulamaz.
ClassCastException fırlatılır, k2'ye hiçbir şey atanmaz; yakalayan olmadığı için program durur, Bitti yazılmaz.
Hayvan h1 = new Kopek(); Hayvan h2 = new Kedi(); Kopek k1 = (Kopek) h1; k1.havla(); Kopek k2 = (Kopek) h2; k2.havla(); System.out.println("Bitti");
Tip Dönüşümü
instanceof ile Güvenli Dönüşüm
hayvanlar = { new Kopek(), new Kedi(), new Hayvan() }
for (Hayvan x : hayvanlar) { if (x instanceof Kopek) { Kopek kk = (Kopek) x; kk.havla(); } else if (x instanceof Kedi) { ((Kedi) x).tirmala(); } else { System.out.println("Özel davranış yok"); } }
Köpek havlıyor Kedi tırmalıyor Özel davranış yok
referans instanceof Tur: nesneTur'den ya da onun bir alt sınıfındansatrue.- Önce sınama, sonra dönüşüm:
ClassCastExceptionoluşamaz.null instanceof Turher zamanfalse.
Java 16+: örüntü eşleme (pattern matching)
if (x instanceof Kopek kk) { kk.havla(); // dönüşüm otomatik }
instanceof zincirleri çoğunlukla bir tasarım uyarısıdır: davranışı alt sınıflarda geçersiz kılınan bir metoda taşımak daha iyidir (Alıştırma 5).Tip Dönüşümü · Adım adım kod izleme
instanceof ile Güvenli Dönüşüm: Kod İzleme
Dizinin üç öğesi de Hayvan tipinde referans; gösterdikleri nesneler farklı sınıflardan.
İlk tur: x bir Kedi. Kopek sınaması yanlış.
Kedi sınaması doğru; önce sınandığı için (Kedi) x dönüşümü güvenli: Kedi tırmalıyor.
İkinci tur: düz bir Hayvan. Hiçbir alt sınıfın örneği değil: iki sınama da yanlış.
else dalı çalışır; hiçbir dönüşüm denenmediği için hata riski yok.
Üçüncü tur: Kopek sınaması doğru, havla() çalışır. Döngü biter; hiçbir turda ClassCastException oluşmadı.
Hayvan[] dizi = {new Kedi(), new Hayvan(), new Kopek()};
for (Hayvan x : dizi) {
if (x instanceof Kopek) {
((Kopek) x).havla();
} else if (x instanceof Kedi) {
((Kedi) x).tirmala();
} else {
System.out.println("Özel davranış yok");
}
}Tip Dönüşümü
Diziler ve ArrayStoreException
Kopek[] kopekler = new Kopek[2]; Hayvan[] hayvanlar = kopekler; // derlenir hayvanlar[0] = new Kopek(); System.out.println("ilk atama tamam"); hayvanlar[1] = new Kedi(); // derlenir ama... System.out.println("buraya gelinmez");
ilk atama tamam Exception in thread "main" java.lang.ArrayStoreException: Kedi
Aynı fikir jenerik listede
List<Hayvan> l = new ArrayList<Kopek>();error: incompatible types: ArrayList<Kopek> cannot be converted to List<Hayvan>
- Diziler kovaryanttır:
KopekbirHayvanolduğundanKopek[]de birHayvan[]sayılır ve yukarı dönüşüm derlenir. - Ama dizi nesnesi gerçekte
Kopek[]'dir.Hayvan[]referansı üzerinden içine birKediyazmak derleyicinin gözünde geçerlidir; JVM her yazmada öğenin tipini denetler veArrayStoreExceptionfırlatır. - Jenerik koleksiyonlar bu açığı derleme zamanında kapatır:
List<Kopek>,List<Hayvan>değildir. - Okumak için kovaryans güvenlidir (her öğe bir
Hayvan'dır); sorun yalnızca yazarken çıkar.
Dinamik Bağlama
Dinamik Bağlama (Dynamic Binding)
- Referans bir alt sınıf nesnesini gösteriyorsa, alt sınıfın metodu çağrılır.
- Çağrılacak sürüm, değişkenin bildirilen tipine göre değil, nesnenin gerçek sınıfına göre belirlenir.
- Bu seçim derleme sırasında değil, çalışma zamanında yapıldığı için dinamik (geç) bağlama denir.
Hayvan h = new Kopek(); h.kos(); // Kopek sınıfının metodu h.sesCikar(); // Kopek sınıfının metodu h.uyu(); // Kopek'te yok → Hayvan'ınki
Köpek koşuyor Hav hav! Hayvan uyuyor
h.kos() iki aşamada çözülür
h'nin tipine (Hayvan) bakar: kos() var mı? Var → çağrı derlenir. Yoksa derleme hatası.Kopek → Kopek.kos() çalışır.Dinamik Bağlama
Metot Araması Hiyerarşinin En Altından Başlar
Hayvan h = new Kopek(); h.kos(); h.uyu(); System.out.println(h.toString()); // h.havla(); → derleme hatası
Nesne bir Kopek: aramalar hep en alttan, gerçek sınıftan başlar.
kos() Kopek'te var → arama biter, Kopek sürümü çalışır.
uyu() Kopek'te yok → bir üste çık: Hayvan'da bulundu.
toString() en tepede, Object'te bulunur: sınıf adı + @ + onaltılık hash kodu (çalıştırmaya göre değişir).
h.havla() derlenmez: derleyici aramayı referans tipi Hayvan'dan yukarı yapar ve bulamaz.
Dinamik Bağlama
Çok Biçimli Dizi: Adım Adım Kod İzleme
Hayvan[] hayvanlar = new Hayvan[3]; hayvanlar[0] = new Kopek(); hayvanlar[1] = new Kedi(); hayvanlar[2] = new Hayvan(); for (int i = 0; i < hayvanlar.length; i++) { hayvanlar[i].sesCikar(); }
Hayvan tipinde 3 öğeli dizi: öğeler referanstır, başlangıçta null.
0. öğe bir Kopek'i gösterir (yukarı dönüşüm, otomatik).
1. öğe bir Kedi'yi gösterir.
2. öğe düz bir Hayvan nesnesidir. Dizi artık üç farklı biçim tutuyor.
i = 0, 0 < 3 doğru → döngü gövdesi.
Aynı satır, farklı nesne: JVM nesnenin sınıfına bakar → Kopek.sesCikar().
i = 1, 1 < 3 doğru → döngü gövdesi.
Aynı satır, farklı nesne: JVM nesnenin sınıfına bakar → Kedi.sesCikar().
i = 2, 2 < 3 doğru → döngü gövdesi.
Aynı satır, farklı nesne: JVM nesnenin sınıfına bakar → Hayvan.sesCikar().
i = 3, 3 < 3 yanlış → döngü biter. Tek satır kod, üç farklı davranış.
Dinamik Bağlama
Perde Arkası: Metot Tablosu
- JVM'ler dinamik bağlamayı genellikle sınıf başına bir metot tablosuyla (virtual method table) uygular: her geçersiz kılınabilir metodun sabit bir sıra numarası (yuvası) vardır.
- Alt sınıfın tablosu üst sınıfınkinin kopyasıyla başlar; geçersiz kılınan yuvalar yeni gövdeyle değiştirilir, yeni metotlar sona eklenir.
- Her nesne kendi sınıfını bilir.
h.kos()için JVM: nesne → sınıfı → tablodakikosyuvası → gövde. Arama tek bir dizi erişimidir, hiyerarşide yukarı yürümek gerekmez.
javap -c Ana çıktısından
47: invokevirtual #15 // Method Hayvan.kos:()V 52: invokevirtual #18 // Method Hayvan.sesCikar:()V
Derleyici bayt koduna yalnızca Hayvan.kos yazar; gövdeyi invokevirtual, nesnenin tablosuna bakarak seçer. Yapıcılar ve super.m() invokespecial, static metotlar invokestatic ile çağrılır: tabloya bakılmaz. private metotlar Java 11'den beri invokevirtual kullanır, ama tek adayları vardır.
Dinamik Bağlama · Adım adım kod izleme
Metot Tablosu ile Çağrı: Kod İzleme
Dizideki iki nesne farklı sınıflardandır; JVM için her biri kendi sınıfının tablosuna giden bir bağlantı taşır.
Bayt kodu “Hayvan.kos, yuva 1” der. Nesne bir Kopek: onun tablosunun 1. yuvasında Kopek.kos vardır.
Aynı yol, 0. yuva: Hav hav!
Aynı bayt kodu, başka nesne. Kedi kos'u geçersiz kılmadığı için tablosunun 1. yuvası Hayvan.kos'u gösterir.
Miyav! Dört çağrının hiçbirinde if ya da tip sınaması yapılmadı: seçimi tablolar yaptı.
Hayvan[] d = {new Kopek(), new Kedi()};
for (Hayvan h : d) {
h.kos();
h.sesCikar();
}Dinamik Bağlama
Neler Dinamik Bağlanmaz?
class Hayvan { String tur = "hayvan"; static void bilgi() { System.out.println("Hayvan.bilgi()"); } void tanit() { System.out.println("Ben bir " + tur); } } class Kopek extends Hayvan { String tur = "köpek"; // gizler, ezmez static void bilgi() { System.out.println("Kopek.bilgi()"); } @Override void tanit() { System.out.println("Ben bir " + tur); } }
Hayvan h = new Kopek();
System.out.println(h.tur);
h.bilgi();
h.tanit();hayvan Hayvan.bilgi() Ben bir köpek
| Üye | Neye göre seçilir? |
|---|---|
| Örnek metodu | Nesne tipi (dinamik) |
| Alan (değişken) | Referans tipi (statik) |
static metot | Referans tipi (statik) |
private / final | Geçersiz kılınamaz, static |
static metot sınıf adıyla çağrılır: Kopek.bilgi(). Alt sınıfta aynı adla alan tanımlamaktan kaçınılır.
Dinamik Bağlama · Adım adım kod izleme
Alan Gizleme ve static: Kod İzleme
Alt sınıftaki tur üst sınıfınkini ezmez, gizler: nesnede iki ayrı alan vardır.
Alanlar dinamik bağlanmaz: h'nin bildirilen tipi Hayvan olduğu için Hayvan.tur okunur: hayvan.
static metot sınıfa aittir; derleyici h.bilgi()'yi Hayvan.bilgi() olarak bağlar.
Örnek metodu dinamik bağlanır: Kopek.tanit() çalışır; onun içinde tur, Kopek'in alanıdır: Ben bir köpek.
Dönüşüm bildirilen tipi Kopek yapar; aynı nesnenin diğer alanı okunur: köpek. Bu karışıklık yüzünden alt sınıfta aynı adlı alan tanımlanmaz.
class Hayvan { String tur = "hayvan"; static String bilgi() { return "Hayvan.bilgi"; } String tanit() { return "Ben bir " + tur; } } class Kopek extends Hayvan { String tur = "köpek"; // gizler static String bilgi() { return "Kopek.bilgi"; } String tanit() { return "Ben bir " + tur; } } Hayvan h = new Kopek(); // main System.out.println(h.tur); System.out.println(h.bilgi()); System.out.println(h.tanit()); System.out.println(((Kopek) h).tur);
Dinamik Bağlama
Yapıcıdan Geçersiz Kılınabilir Metot Çağırmak
- Dinamik bağlama nesne kurulurken de çalışır: üst sınıfın yapıcısındaki
ciz()çağrısı, nesne birEtiketolduğu içinEtiket.ciz()'i çalıştırır. - Ama o anda alt sınıfın alan ilk değerleri henüz atanmamıştır:
metinhâlânull. Alt sınıfın metodu, yarı kurulmuş bir nesne üzerinde çalışır. - Metot alana yalnızca yazdırmak için değil, kullanmak için dokunsaydı program çökerdi.
- Kural: yapıcıdan yalnızca
private,finalya dastaticmetotlar çağrılmalıdır; bunlar geçersiz kılınamaz.
ciz() içinde metin.length() kullanılsaydı
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "this.metin" is null at Etiket.ciz(Ana.java:21) at Bilesen.<init>(Ana.java:16) at Etiket.<init>(Ana.java:19)
Etiket yapıcısı → Bilesen yapıcısı → yine Etiket'in metodu. <init>, bayt kodunda yapıcının adıdır.Dinamik Bağlama · Adım adım kod izleme
Yapıcıdaki Çağrı: Kod İzleme
new nesneye yer ayırır, metin varsayılan null. Etiket()'in ilk işi gizli super() çağrısıdır.
Bilesen() gövdesi çalışır ve ilk satırı yazar.
ciz() = this.ciz(). Nesnenin sınıfı Etiket olduğu için geçersiz kılınmış sürüm seçilir; ama metin henüz atanmadı: Etiket: null.
Üst sınıf yapıcısı döndükten sonra alt sınıfın alan ilk değeri çalışır: metin = "Merhaba". Çok geç: ciz() çoktan çağrıldı.
Etiket() gövdesi çalışır; nesne artık tamamen kuruldu.
Kurulmuş nesnede aynı metot doğru sonucu verir. Fark yalnızca çağrının ne zaman yapıldığıdır.
class Bilesen { Bilesen() { System.out.println("Bilesen kuruluyor"); ciz(); } void ciz() { System.out.println("boş bileşen"); } } class Etiket extends Bilesen { private String metin = "Merhaba"; Etiket() { System.out.println("Etiket kuruldu"); } @Override void ciz() { System.out.println("Etiket: " + metin); } } Etiket e = new Etiket(); // main e.ciz();
Aşırı Yükleme
Aşırı Yükleme Derleme Zamanında Seçilir
static void yazdir(Hayvan h) { System.out.println("yazdir(Hayvan)"); } static void yazdir(Kopek k) { System.out.println("yazdir(Kopek)"); } public static void main(String[] args) { Kopek kopek = new Kopek(); Hayvan hayvan = kopek; yazdir(kopek); yazdir(hayvan); yazdir((Kopek) hayvan); }
yazdir(Kopek) yazdir(Hayvan) yazdir(Kopek)
hayvanbirKopek'i gösterse deyazdir(hayvan)→yazdir(Hayvan).- Aşırı yüklenmiş sürümler arasındaki seçim argümanın bildirilen tipine göre, derleyici tarafından yapılır.
- Dönüşüm bildirilen tipi değiştirir:
(Kopek) hayvan→yazdir(Kopek).
| Aşırı yükleme | Geçersiz kılma | |
|---|---|---|
| Nerede? | Aynı sınıf | Alt sınıf |
| İmza | Farklı parametre | Aynı imza |
| Seçim | Derleyici | JVM |
| Ölçüt | Bildirilen tip | Gerçek tip |
Aşırı Yükleme
Aşırı Yükleme: En Özel Sürüm ve Belirsizlik
static void goster(Object o) { System.out.println("goster(Object)"); } static void goster(Hayvan h) { System.out.println("goster(Hayvan)"); } static void goster(String s) { System.out.println("goster(String)"); } // main içinde: goster(new Kedi()); goster("a"); Object o = "a"; goster(o); goster((Hayvan) null);
goster(Hayvan) goster(String) goster(Object) goster(Hayvan)
goster(null);
error: reference to goster is ambiguous both method goster(Hayvan) in Ana and method goster(String) in Ana match
- Birden çok sürüm uyarsa derleyici en özel olanı seçer:
KedihemHayvan'a hemObject'e uyar;Hayvandaha özeldir. nullher başvuru tipine uyar.HayvanileStringarasında kalıtım ilişkisi olmadığı için hiçbiri ötekinden özel değildir: belirsiz çağrı, derleme hatası.- Uyan sürümler tek bir zincir üzerindeyse (
Object,Hayvan,Kedi) belirsizlik olmaz:goster(null)en alttakini,goster(Kedi)'yi seçer. - Çözüm, ifadenin bildirilen tipini açık dönüşümle belirlemektir:
goster((Hayvan) null).
Aşırı Yükleme · Adım adım kod izleme
Derleyici mi, JVM mi? Kod İzleme
İki değişkenin bildirilen tipi aynı (Hayvan); gösterdikleri nesneler farklı: Kopek ve Kedi.
hayvan bir Kopek'i gösterse de derleyici yalnızca bildirilen tipi bilir: yazdir(Hayvan).
Metodun içinde h.sesCikar() geçersiz kılınmış bir metot: JVM nesneye bakar, Hav hav!
diger da Hayvan tipinde: aynı sürüm seçilir.
Aynı satır, farklı nesne: bu kez Kedi.sesCikar(), Miyav!
Açık dönüşüm ifadenin bildirilen tipini Kopek yapar: derleyici bu kez yazdir(Kopek)'u seçer.
Aşırı yükleme seçimi bildirilen tiple derlemede, geçersiz kılma seçimi gerçek tiple çalışırken yapıldı.
static void yazdir(Hayvan h) { System.out.print("yazdir(Hayvan): "); h.sesCikar(); } static void yazdir(Kopek k) { System.out.print("yazdir(Kopek): "); k.sesCikar(); } // main içinde: Hayvan hayvan = new Kopek(); Hayvan diger = new Kedi(); yazdir(hayvan); yazdir(diger); yazdir((Kopek) hayvan);
Aşırı Yükleme
Aşırı Yükleme Seçiminin Üç Aşaması
Yalnızca tam eşleşme, ilkel genişletme (int → long) ve başvuru genişletmesi (String → Object) denenir.
1. aşamada aday yoksa kutulama ve kutudan çıkarma da denenir: double → Double → Object.
Hâlâ aday yoksa int... gibi değişken sayıda parametreli sürümler devreye girer.
- Bir aşamada en az bir aday bulunursa sonraki aşamalara geçilmez; o aşamanın adayları arasından en özel olan seçilir.
- Sonuç:
f(long)vef(Integer)varkenf(5)çağrısıf(long)'u seçer. Genişletme kutulamadan önce gelir, çünkü kutulama Java 5'te eklendi ve eski programların davranışı değişmesin istendi. - Daraltma (
long→int) hiçbir aşamada denenmez; üç aşamada da aday yoksa:no suitable method found for yazdir(boolean). - Bu sıra derleme zamanında uygulanır; seçilen imza bayt koduna yazılır. Seçilen imzanın hangi sınıftaki gövdesinin çalışacağı ise çalışma zamanında belirlenir.
Aşırı Yükleme · Adım adım kod izleme
Aşırı Yükleme Aşamaları: Kod İzleme
5 bir int. 1. aşamada yalnızca genişletme ile long uyar; Integer ve Object kutulama ister, bu aşamada denenmez.
byte da long'a genişler. Kutulansaydı bile Byte olurdu, Integer değil.
Argüman zaten bir Integer: 1. aşamada iki aday var. Integer, Object'ten özel olduğu için seçilir.
String yalnızca Object'e uyar.
double, long'a daraltılamaz; 1. aşamada aday yok. 2. aşamada Double'a kutulanır ve Object'e uyar.
Sıfır ya da iki argümanı yalnızca int... karşılar; ona en son, 3. aşamada başvurulur.
static void f(long x) { p("f(long)"); } static void f(Integer x) { p("f(Integer)"); } static void f(Object x) { p("f(Object)"); } static void f(int... x) { p("f(int...)"); } // main içinde (p: System.out.println): byte b = 1; f(5); f(b); f(Integer.valueOf(5)); f("x"); f(2.5); f(); f(1, 2);
Kalıtım ve Çok Biçimlilik
Kalıtım ve Çok Biçimlilik
- Kalıtım, sınıf hiyerarşileri oluşturmak için gereklidir.
- Çok biçimlilik için kalıtım gereklidir: bir sınıf hiyerarşisine sahip olmalıyız.
- Kalıtımı kullanmak için çok biçimlilik gerekli değildir: yalnızca kod yeniden kullanımı için de kalıtım yapılabilir.
- B sınıfı A'nın alt sınıfıysa, B'nin nesnesi A'nın nesnesi gibi davranabilir (tersi değil).
- A referansını B referansına atamak için daraltma dönüşümü (casting) gerekir; parantezle yapılır.
A objA = new B(); // B, A gibi davranır B objB = (B) objA; // geçerli, ancak // dikkatli kullanılmalı
objA gerçekten bir B nesnesini göstermiyorsa ClassCastException fırlatılır.Java'da arayüzler (interface) de çok biçimlilik sağlar: implements eden her sınıf, arayüz tipindeki referansa atanabilir.
Kalıtım ve Çok Biçimlilik
Örnek: Şekil Hiyerarşisi
abstract class Sekil { private String isim; public Sekil(String isim) { this.isim = isim; } public String getIsim() { return isim; } public abstract double alanHesapla(); public abstract double cevreHesapla(); public void ciz() { System.out.println(isim + " çiziliyor."); } }
abstract: gövdesiz metot; her alt sınıf yazmak zorundadır, new Sekil(…) yapılamaz.
Kalıtım ve Çok Biçimlilik
Alt Sınıflar Metotları Geçersiz Kılar
class Kare extends Sekil { private double kenar; public Kare(double kenar) { super("Kare"); this.kenar = kenar; } @Override public double alanHesapla() { return kenar * kenar; } @Override public double cevreHesapla() { return 4 * kenar; } @Override public void ciz() { System.out.println("Kare çiziliyor: kenar " + kenar); } }
Kare: yapıcı super("Kare") ile ismi üst sınıfa verir; üç metodu da geçersiz kılar.
class Dikdortgen extends Sekil { private double genislik; private double yukseklik; public Dikdortgen(double genislik, double yukseklik) { super("Dikdörtgen"); this.genislik = genislik; this.yukseklik = yukseklik; } @Override public double alanHesapla() { return genislik * yukseklik; } @Override public double cevreHesapla() { return 2 * (genislik + yukseklik); } // ciz() ezilmedi → Sekil'inki kullanılır }
Dikdortgen: alan ve çevreyi geçersiz kılar, ciz()'i geçersiz kılmaz → Sekil.ciz() kullanılır.
class Cember extends Sekil { private double yaricap; public Cember(double yaricap) { super("Çember"); this.yaricap = yaricap; } @Override public double alanHesapla() { return Math.PI * yaricap * yaricap; } @Override public double cevreHesapla() { return 2 * Math.PI * yaricap; } @Override public void ciz() { System.out.println("Çember çiziliyor: yarıçap " + yaricap); } }
Cember: Math.PI ile hesaplar; ciz()'i kendi bilgisiyle geçersiz kılar.
Kalıtım ve Çok Biçimlilik
Şekiller Dizisi: Adım Adım Kod İzleme
Sekil[] sekiller = new Sekil[3]; sekiller[0] = new Kare(5); sekiller[1] = new Dikdortgen(4, 6); sekiller[2] = new Cember(7); for (Sekil sekil : sekiller) { System.out.println(sekil.getIsim() + " alanı: " + sekil.alanHesapla()); sekil.ciz(); }
Dizi Sekil tipinde; öğeler üç farklı alt sınıf nesnesini gösterir.
sekil.alanHesapla() → Kare.alanHesapla() çalışır.
ciz() geçersiz kılınmış → Kare.ciz() çalışır.
sekil.alanHesapla() → Dikdortgen.alanHesapla() çalışır.
Dikdortgen ciz()'i geçersiz kılmadı → arama yukarı çıkar, Sekil.ciz() çalışır.
sekil.alanHesapla() → Cember.alanHesapla() çalışır.
ciz() geçersiz kılınmış → Cember.ciz() çalışır.
Kalıtım ve Çok Biçimlilik
Neden Çok Biçimlilik?
Tip sınayarak
static double alanKotu(Sekil s) { double alan; if (s instanceof Kare) { alan = ((Kare) s).alanHesapla(); } else if (s instanceof Dikdortgen) { alan = ((Dikdortgen) s).alanHesapla(); } else if (s instanceof Cember) { alan = ((Cember) s).alanHesapla(); } else { alan = 0; // yeni tür unutuldu! } return alan; }
Yeni bir şekil eklenince bu metot da değişmeli; unutulursa alanKotu(new DikUcgen(3, 4)) sessizce 0.0 döner.
Çok biçimli
static double toplamAlan(Sekil[] dizi) { double toplam = 0; for (Sekil sekil : dizi) { // her nesne kendi alanını toplam += sekil.alanHesapla(); } return toplam; }
- Yeni
DikUcgen extends Sekilsınıfı eklemek yeter; bu kod hiç değişmez. - { Kare(5), Dikdortgen(4, 6), DikUcgen(3, 4) } → 55.0
- Kod kısalır,
ClassCastExceptionriski ortadan kalkar.
İlke: genişletmeye açık, değiştirmeye kapalı kod (open/closed principle).
Kalıtım ve Çok Biçimlilik
Çok Biçimli Parametre ve Dönüş Değeri
class Veteriner { void muayene(Hayvan h) { // her alt sınıf System.out.print("Muayene: "); h.sesCikar(); } static Hayvan sahiplen(String tur) { // gizli if (tur.equals("kedi")) return new Kedi(); return new Kopek(); } } // main içinde: Veteriner v = new Veteriner(); v.muayene(new Kedi()); Hayvan h = Veteriner.sahiplen("köpek"); v.muayene(h); System.out.println(h.getClass().getSimpleName());
Muayene: Miyav! Muayene: Hav hav! Kopek
- Parametre üst sınıf tipinde tanımlanırsa metot, bugün var olan ve ileride yazılacak her alt sınıfın nesnesini kabul eder.
muayeneyeni birKussınıfı için değişmez. - Dönüş tipi üst sınıf olursa metot hangi alt sınıfı ürettiğini gizler. Çağıran yalnızca
Hayvansözleşmesine güvenir;sahiplenbaşka bir alt sınıf döndürmeye başlasa kullanan kod etkilenmez. - Nesneleri üreten bu tür metotlara fabrika metodu (factory method) denir.
- Genel ilke: parametre ve değişken tipleri, işi görmeye yeten en genel tip olarak seçilir.
Kalıtım ve Çok Biçimlilik
Örnek: Tanksavar Oyunu
TS Jawelin: TanksavarJawelin, TS Tow2A: TanksavarTow2A. Tank sınıfının da TankT90MS ve TankLeopard2A4 alt sınıfları vardır.
- Swing ile yazılmış oyunda düşen tanklar vurulur; skor 10'un katı olunca tanksavar türü değişir.
- Oyun döngüsü yalnızca
TanksavarveTanktiplerini bilir.
Tanksavar tanksavar = new TanksavarJawelin(x, y); ... // hangi top? nesneye bağlı (dinamik bağlama) toplar.add(tanksavar.topAt()); ... if (tanksavar instanceof TanksavarJawelin) { tanksavar = new TanksavarTow2A(tanksavar.x, tanksavar.y); }
Sınama çıktısı
Top t1 = tanksavar.topAt(); // Jawelin // → Top37mm, yükseklik 30 tanksavar = new TanksavarTow2A(100, 540); Top t2 = tanksavar.topAt(); // → Top57mm, yükseklik 40
topAt() çağrısı, tanksavar değişince farklı bir top üretir; oyun kodunda hiçbir if gerekmez.Kalıtım ve Çok Biçimlilik
equals ve Kalıtım: Simetri Tuzağı
Object metotları da dinamik bağlanır: Object o = k1; System.out.println(o); nesnenin kendi toString'ini, o.equals(x) kendi equals'ını çalıştırır.
class Nokta { final int x, y; Nokta(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { if (!(o instanceof Nokta)) return false; Nokta n = (Nokta) o; return x == n.x && y == n.y; } @Override public int hashCode() { return 31 * x + y; } } class RenkliNokta extends Nokta { final String renk; RenkliNokta(int x, int y, String renk) { super(x, y); this.renk = renk; } @Override public boolean equals(Object o) { if (!(o instanceof RenkliNokta)) return false; RenkliNokta r = (RenkliNokta) o; return super.equals(r) && renk.equals(r.renk); } }
Nokta p = new Nokta(1, 2); RenkliNokta k = new RenkliNokta(1, 2, "kırmızı"); System.out.println(p.equals(k) + " " + k.equals(p));
true false
- Hangi
equals'ın çalışacağını alıcı nesne belirler:p.equals(k)Nokta'nınkini,k.equals(p)RenkliNokta'nınkini çalıştırır. - Sonuçlar farklı: simetri bozuldu. Bir
HashSetya dacontains, elemanların sırasına göre farklı cevap verebilir. - Çözümler: iki sınıfta da
getClass()karşılaştırması; ya da kalıtım yerine oluşum (RenkliNoktabirNoktaalanı tutar).
Özet
Özet
| Kavram | Kural | Örnek |
|---|---|---|
| Referans kuralı | Üst sınıf referansı alt sınıf nesnesini gösterebilir; tersi derlenmez | Hayvan h = new Kopek(); |
| is-a | Alt sınıf bir üst sınıftır; tersi değil | h instanceof Hayvan → true |
| Yukarı dönüşüm | Otomatik ve her zaman güvenli | Object o = kopek; |
| Aşağı dönüşüm | Açık yazılır; nesne uygun değilse ClassCastException | Kopek k = (Kopek) h; |
| Erişim | Hangi metotların çağrılabileceğini referans tipi belirler | h.havla() → derleme hatası |
| Dinamik bağlama | Hangi sürümün çalışacağını nesne tipi belirler; arama alttan yukarı | h.kos() → Kopek sürümü |
| Statik bağlama | Alanlar, static/private/final metotlar ve aşırı yükleme seçimi | h.tur → "hayvan" |
| Fayda | Üst sınıf tipiyle bir kez yazılan kod, yeni alt sınıflarla değişmeden çalışır | toplamAlan(sekiller) |
Özet
İlgili Sayfalar
Simülatör: üst sınıf tipindeki referansla yapılan çağrıda metot aramasının nesnenin gerçek sınıfından başlayıp yukarı çıkması.
Arayüz tanımlama ve implements, arayüz tipindeki referanslarla çok biçimlilik, çoklu arayüz, default/static/private arayüz metotları, jenerikler.
Bir sınıf tek bir üst sınıftan türeyebilir, ama birçok arayüzü uygulayabilir: Hayvan h yerine SesCikarabilen s gibi arayüz tipleriyle de aynı dinamik bağlama çalışır.
Özet
Sık Yapılan Hatalar (1): Derleme Hataları
| Kod | Derleyici mesajı | Neden / çözüm |
|---|---|---|
Kopek k = new Hayvan(); | incompatible types: Hayvan cannot be converted to Kopek | Her hayvan köpek değildir; ters atama yapılamaz. |
Kopek k = hayvan; | incompatible types: Hayvan cannot be converted to Kopek | Nesne köpek olsa bile açık dönüşüm gerekir: (Kopek) hayvan. |
hayvan.havla(); | cannot find symbol … variable hayvan of type Hayvan | Referans tipinde metot yok; önce aşağı dönüşüm gerekir. |
(Kopek) h.havla(); | not a statement | Parantez eksik: ((Kopek) h).havla(); |
(Kedi) kopek | incompatible types: Kopek cannot be converted to Kedi | Kardeş sınıflar arasında dönüşüm yoktur. |
@Override void sescikar() | method does not override or implement a method from a supertype | Ad yanlış yazıldı (büyük/küçük harf). |
void sesCikar() (üstte public) | attempting to assign weaker access privileges; was public | Geçersiz kılan metot erişimi daraltamaz. |
Özet
Sık Yapılan Hatalar (2): Çalışma Zamanı ve Mantık
(Kopek) h yazıp h'nin bir Kedi gösterebileceğini unutmak → ClassCastException. Önce instanceof.
(Hayvan) kopek köpeği hayvana çevirmez; sesCikar() yine "Hav hav!" yazar.
h.tur referans tipine göre okunur ("hayvan"). Çözüm: alanlar private yapılır, getter metodu geçersiz kılınır.
Alt sınıfta equals(Kisi k) yazmak equals(Object)'i geçersiz kılmaz, aşırı yükler; koleksiyonlar bu metodu çağırmaz. @Override bu hatayı yakalar.
== ile nesne karşılaştırmak== referansları karşılaştırır; içerik için geçersiz kılınmış equals kullanılır.
instanceof zinciriYeni alt sınıf eklendikçe kırılır. Davranış, geçersiz kılınan bir metoda taşınmalıdır.
Özet
Sık Yapılan Hatalar (3)
Alt sınıfın metodu, alanları henüz atanmamış nesnede çalışır: null çıktısı ya da NullPointerException.
Hayvan[] d = new Kopek[2]; d[1] = new Kedi(); derlenir, çalışırken ArrayStoreException: Kedi.
goster(null), ilişkisiz iki aşırı yükleme varken: reference to goster is ambiguous. Seçim açık dönüşümle yapılır.
f(long) ve f(Integer) varken f(5) → f(long): genişletme 1. aşamadadır.
p.equals(k) true, k.equals(p) false: simetri bozulur, koleksiyonlar tutarsızlaşır.
List<Hayvan> l = new ArrayList<Kopek>(); → incompatible types. Jenerikler kovaryant değildir.
Özet
Örnek Problem 1: Çıktı Nedir?
class A { void yaz() { System.out.println("A.yaz"); } void cagir() { yaz(); // this.yaz() } } class B extends A { @Override void yaz() { System.out.println("B.yaz"); } } // main içinde: A a = new B(); a.cagir(); new A().cagir();
cagir() yalnızca A'da tanımlı ve içinden yaz()'ı çağırıyor. B ise yalnızca yaz()'ı eziyor. Program ne yazdırır?
Cevap
B.yaz A.yaz
a.cagir():B'decagiryok →A.cagir()çalışır.- İçerideki
yaz()aslındathis.yaz()'dır;thisbirBnesnesi → dinamik bağlama ileB.yaz(). new A().cagir(): nesneA→A.yaz().- Ders: üst sınıfın kodu, alt sınıfın geçersiz kıldığı metodu farkında olmadan çağırabilir.
Özet
Örnek Problem 2: Derlenir mi, Çalışır mı?
Hayvan h = new Kedi(); Kedi k1 = h; // (1) Kedi k2 = (Kedi) h; // (2) Kopek k3 = (Kopek) h; // (3) h.tirmala(); // (4) ((Kedi) h).tirmala(); // (5) Object o = new Kopek(); o.sesCikar(); // (6) ((Hayvan) o).sesCikar(); // (7)
Her satır için karar verin: derleme hatası, çalışma zamanı hatası ya da sorunsuz (çıktısıyla).
Cevap
| # | Sonuç | Neden |
|---|---|---|
| 1 | Derleme | Hayvan → Kedi için açık dönüşüm gerekir |
| 2 | Sorunsuz | Nesne gerçekten Kedi |
| 3 | Çalışma | ClassCastException: Kedi → Kopek olmaz |
| 4 | Derleme | Hayvan'da tirmala() yok |
| 5 | Sorunsuz | "Kedi tırmalıyor" |
| 6 | Derleme | Object'te sesCikar() yok |
| 7 | Sorunsuz | "Hav hav!" (dinamik bağlama) |
Özet
Örnek Problem 3: Aşırı Yükleme + Geçersiz Kılma
class Ust { void f(Object o) { System.out.println("Ust.f(Object)"); } void f(String s) { System.out.println("Ust.f(String)"); } } class Alt extends Ust { @Override void f(Object o) { System.out.println("Alt.f(Object)"); } } // main içinde: Ust u = new Alt(); Object o = "merhaba"; u.f(o); u.f("merhaba");
Ust'te iki aşırı yüklenmiş f var; Alt yalnızca f(Object)'i eziyor. İki çağrı ne yazdırır?
Cevap
Alt.f(Object) Ust.f(String)
- Derleyici sürümü seçer:
o'nun bildirilen tipiObject→f(Object). Çalışırken nesneAlt→ geçersiz kılınmış sürüm: Alt.f(Object). - Argüman
String→ en uygun sürümf(String).Altbunu geçersiz kılmadığı için Ust.f(String).
Önce aşırı yükleme (derleme zamanı, bildirilen tip), sonra geçersiz kılma (çalışma zamanı, nesne tipi).
Özet
Örnek Problem 4: Kurulum, Aşama, Dizi
class Ust { Ust() { goster(); } void goster() { System.out.println("Ust"); } } class Alt extends Ust { private int[] dizi = {1, 2, 3}; @Override void goster() { System.out.println(dizi == null ? "dizi yok" : "uzunluk " + dizi.length); } }
new Alt();ve ardındannew Alt().goster();ne yazdırır?f(long),f(Integer),f(Object)vef(int...)varkenshort s = 3; f(s);,f('A');vef(Long.valueOf(4));hangi sürümleri seçer?Object[] d = new String[1]; d[0] = 1;derlenir mi, çalışır mı?
Cevapları göster
- dizi yok, sonra dizi yok ve uzunluk 3: her
new Alt()sırasındaUst(),Alt.goster()'idiziatanmadan çağırır; kurulumdan sonraki çağrıda dizi hazırdır. - f(long), f(long), f(Object):
shortvechar,long'a genişler.Longiçinf(long)kutudan çıkarma ister; 1. aşamada tek adayObject'tir. - Derlenir, çalışırken
ArrayStoreException: java.lang.Integer: 1,Integer'a kutulanır ve gerçekteString[]olan diziye yazılamaz.
Özet
Alıştırmalar (1)
Calisan(maas()= temel maaş),Mudur(temel × 1,5) veStajyer(sabit 5000) sınıflarını yazın.Calisan[]= { Ayşe 20000, Müdür Mehmet 30000, Stajyer Can } için toplam maaş?X:ad()→ "X",yaz()→println(ad()).Y extends X:ad()→ "Y".Z extends Y:yaz()→println("Z:" + ad()). { new X(), new Y(), new Z() } için her birindeyaz()çağrılırsa çıktı?Object o = new Kopek();ardındano.sesCikar();neden derlenmez? Nasıl düzeltilir, düzeltilince ne yazar?
Cevaplar / ipuçları
Mudur.maas(),return temelMaas * 1.5;ile geçersiz kılınır; döngüdetoplam += c.maas();→ 20000 + 45000 + 5000 = 70000.0.- X, Y, Z:Y. Y'de
yaz()X'ten gelir ama içindekiad()Y'ninkidir; Z'dead()Y'den gelir. Object'tesesCikar()yok (cannot find symbol).((Hayvan) o).sesCikar();→ Hav hav!
Özet
Alıştırmalar (2)
- Şekil hiyerarşisine
DikUcgen(a, b)ekleyin (alan = a·b/2, çevre = a + b + hipotenüs).DikUcgen(3, 4)için alan, çevre veciz()çıktıları nedir? Şekil döngüsünde ne değişir? instanceofzincirli döngüyü (Kopek → havla, Kedi → tırmala, diğer → "Özel davranış yok") hiçinstanceofkullanmadan yazın.yazdir(Hayvan)veyazdir(Kopek)aşırı yüklemeleri varkenObject o = new Kopek(); yazdir(o);derlenir mi?
Cevaplar / ipuçları
Dik üçgen alanı: 6.0,Dik üçgen çevresi: 12.0,Dik üçgen çiziliyor.(ciz()geçersiz kılınmadıysa). Döngü hiç değişmez; yalnızca diziye yeni nesne eklenir.Hayvan'avoid ozelDavranis()("Özel davranış yok") ekleyin;KopekveKedibunu ezsin. Döngü:x.ozelDavranis();→ aynı üç satır.- Hayır: bildirilen tip
Object; iki sürüm deObjectkabul etmez (no suitable method found for yazdir(Object)).yazdir((Kopek) o)ile derlenir.
Özet
Kaynaklar
- Oracle, The Java Tutorials: Polymorphism; Overriding and Hiding Methods; Inheritance.
- J. Gosling ve diğ., The Java Language Specification, Java SE 21 Edition (5.5 Casting Contexts, 8.4.8 Inheritance, Overriding, and Hiding, 15.12 Method Invocation Expressions).
- P. Deitel ve H. Deitel, Java: How to Program, 11. baskı, Pearson (Bölüm 10: Object-Oriented Programming: Polymorphism and Interfaces).
- K. Sierra ve B. Bates, Head First Java, 3. baskı, O'Reilly (Inheritance and Polymorphism).
- C. S. Horstmann, Core Java, Volume I: Fundamentals, 12. baskı, Pearson (Bölüm 5: Inheritance).
- A. Downey ve C. Mayfield, Think Java, 2. baskı, O'Reilly.