Bölüm 4: Çok Biçimlilik
Çoktan Seçmeli · Çok Biçimlilik Kavramı
Soru 1: Kim Seçer?
Çalışma zamanı çok biçimliliği için hangisi doğrudur?
A: Geçersiz kılma + dinamik bağlama çalışma zamanında, nesnenin gerçek tipine göre çözülür; bunun için sınıf hiyerarşisi gerekir. Aşırı yükleme ise derleme zamanında, argümanların bildirilen tiplerine göre derleyicinin seçimidir (B ve D onu anlatıyor).
Çoktan Seçmeli · Referanslar ve Tip Uyumu
Soru 2: Hangi Atama Derlenir?
Kopek ve Kedi, Hayvan’dan türüyor. Hangi satır derlenir?
C: Üst sınıf (burada Object) referansı alt sınıf nesnesini gösterebilir. A: her hayvan köpek değildir (Hayvan cannot be converted to Kopek). B: kardeş sınıflar arasında atama yoktur. D: nesne köpek olsa da derleyici h’nin bildirilen tipini (Hayvan) görür; açık dönüşüm gerekir.
Hata Bulma · Referanslar ve Tip Uyumu
Soru 3: Derlenir mi, Çalışır mı?
kenar() yalnızca Kare’de, soyut alan() Sekil’de tanımlı. Her satır için karar verin: derleme hatası, çalışma zamanı hatası ya da sorunsuz.
Sekil s = new Kare(2); Kare k1 = s; // 1 Kare k2 = (Kare) s; // 2 s.kenar(); // 3 Object o = k2; o.alan(); // 4 double a = ((Sekil) o).alan(); System.out.println(a); // 5 Cember c = (Cember) s; // 6
Cevabı göster
| # | Sonuç | Neden |
|---|---|---|
| 1 | Derleme | Sekil → Kare için açık dönüşüm gerekir |
| 2 | Sorunsuz | Nesne gerçekten bir Kare |
| 3 | Derleme | Sekil’de kenar() yok |
| 4 | Derleme | Object’te alan() yok |
| 5 | Sorunsuz | 4.0 yazar (dinamik bağlama) |
| 6 | Çalışma | ClassCastException: Kare, Cember değil |
Derleyici yalnızca referans tipine bakar; dönüşümün geçerliliğini (6) JVM, nesneye bakarak denetler.
Adım Adım Çalıştırma · Tip Dönüşümü
Soru 4: Bir Nesne, Dört Bakış
Hangi dönüşümler başarılı olur? Program nerede durur ve o ana kadar ne yazar?
Tek bir Kopek nesnesi; Object tipindeki o onu gösterir (yukarı dönüşüm, otomatik).
(Hayvan) o aşağı dönüşümdür: JVM nesnenin bir Hayvan olduğunu denetler. Yeni nesne oluşmaz.
Referans tipi Hayvan olsa da nesne Kopek: Kopek.sesCikar() çalışır.
(Kopek) h başarılı; havla() artık çağrılabilir, çünkü referans tipi Kopek.
Üç referans, tek nesne: o == k → true. Dönüşüm nesneyi değil, bakışı değiştirir.
Derleyici izin verir (bir Hayvan pekâlâ Kedi olabilir), ama JVM nesneye bakar: ClassCastException. son hiç yazılmaz.
// Hayvan, Kopek (havla() ekler) ve Kedi // sesCikar()'ı geçersiz kılar. Object o = new Kopek(); Hayvan h = (Hayvan) o; h.sesCikar(); Kopek k = (Kopek) h; k.havla(); System.out.println(o == k); Kedi c = (Kedi) h; System.out.println("son");
Çoktan Seçmeli · Tip Dönüşümü
Soru 5: Derlenir ama…
Kopek k = (Kopek) new Hayvan(); satırı için hangisi doğrudur?
B: Açık dönüşüm, üst tipten alt tipe olduğu için derleyiciye kabul ettirilir. Ama nesne düz bir Hayvan’dır; JVM dönüşüm anında nesnenin gerçek sınıfını denetler ve Hayvan cannot be cast to Kopek der. Üst sınıf nesnesi alt sınıf referansına hiçbir zaman atanamaz.
Çoktan Seçmeli · Tip Dönüşümü
Soru 6: Parantez Nerede?
Hayvan h2 = new Kopek(); sonrasında (Kopek) h2.havla(); yazılıyor. Ne olur?
D: . operatörü dönüşümden önce gelir: ifade (Kopek) (h2.havla()) gibi okunur ve error: not a statement verir; düzeltilse bile Hayvan’da havla() bulunmaz. Doğrusu ((Kopek) h2).havla();.
Çoktan Seçmeli · Dinamik Bağlama
Soru 7: Neler Dinamik Bağlanmaz?
Program ne yazar?
class Ust { String ad = "ust"; static String s() { return "Ust.s"; } String m() { return "Ust.m"; } } class Alt extends Ust { String ad = "alt"; static String s() { return "Alt.s"; } @Override String m() { return "Alt.m"; } } // main içinde: Ust u = new Alt(); System.out.println(u.ad + " " + u.s() + " " + u.m());
C: Yalnızca örnek metotları nesnenin tipine göre (dinamik) seçilir: u.m() → Alt.m. Alan erişimi ve static metot çağrısı referansın bildirilen tipine (Ust) göre derleme zamanında bağlanır: u.ad → ust, u.s() → Ust.s. Alt sınıftaki aynı adlı alan ve static metot yalnızca gizler, geçersiz kılmaz.
Adım Adım Çalıştırma · Dinamik Bağlama
Soru 8: Yapıcıdan Çağrılan yaz()
Ust’un yapıcısı, Alt’ın geçersiz kıldığı yaz()’ı çağırıyor. Program ne yazar? x neden iki farklı değerle görünür?
Nesne ayrılır, x varsayılan 0. Alt()’ın ilk işi gizli super(): x = 5 ilk değeri henüz çalışmadı.
Ust() gövdesi çalışır: Ust.
yaz() = this.yaz(); nesne bir Alt, dinamik bağlama Alt.yaz()’ı seçer. x hâlâ 0: Alt.yaz x=0.
super() döndükten sonra Alt’ın alan ilk değerleri çalışır: x = 5.
Ancak şimdi Alt() gövdesi: Alt 5. Ders: yapıcıdan geçersiz kılınabilen metot çağırmak yarı kurulmuş nesneyi açığa çıkarır.
class Ust { Ust() { System.out.println("Ust"); yaz(); } void yaz() { System.out.println("Ust.yaz"); } } class Alt extends Ust { int x = 5; Alt() { System.out.println("Alt " + x); } @Override void yaz() { System.out.println("Alt.yaz x=" + x); } } // main içinde: new Alt();
Çıktı Sorusu · Dinamik Bağlama
Soru 9: P, Q, R
Q yalnızca b()’yi, R yalnızca a()’yı geçersiz kılıyor. Program üç satırda ne yazar?
class P { void a() { System.out.print("P.a "); b(); } void b() { System.out.print("P.b "); } } class Q extends P { @Override void b() { System.out.print("Q.b "); } } class R extends Q { @Override void a() { System.out.print("R.a "); super.a(); } } // main içinde: P x = new R(); x.a(); System.out.println(); new Q().a(); System.out.println(); new P().a(); System.out.println();
Cevabı göster
R.a P.a Q.b P.a Q.b P.a P.b
x.a(): nesneR→R.a;super.a()P.a’yı çalıştırır (Q’daayok).P.aiçindekib()=this.b();thisbirR, en yakınb()Q’da:Q.b.new Q().a():aP’den gelir,bQ’nunkidir. DüzPnesnesinde ikisi deP’nin.
Çoktan Seçmeli · Dinamik Bağlama
Soru 10: Dinamik Olan Hangisi?
Hayvan h = new Kopek(); için aşağıdakilerden hangisinde çalışacak kod nesnenin tipine göre seçilir?
A: Geçersiz kılınmış örnek metotları dinamik bağlanır. Alanlar ve static metotlar referans tipine göre seçilir; final metodun alt sınıfta başka sürümü olamaz, tek aday zaten Hayvan’ınkidir.
Çoktan Seçmeli · Aşırı Yükleme
Soru 11: Aşırı Yüklemede Bildirilen Tip
yazdir(Hayvan) ve yazdir(Kopek) aşırı yüklenmiş. Hayvan h = new Kopek(); yazdir(h); ne yazar?
D: Aşırı yüklenmiş sürümler arasındaki seçimi derleyici, argümanın bildirilen tipine göre yapar: h Hayvan tipinde. Nesnenin Kopek olması bu seçimi değiştirmez. yazdir((Kopek) h) yazılsaydı yazdir(Kopek) seçilirdi.
Adım Adım Çalıştırma · Aşırı Yükleme
Soru 12: Önce Derleyici, Sonra JVM
Beş çağrının her biri için derleyici hangi imzayı seçer, JVM hangi sürümü çalıştırır?
Tek bir Alt nesnesi; u onu Ust, a Alt olarak görür. o kutulanmış bir Integer’ı gösterir.
Derleyici yalnızca Ust’un metotlarını görür; o’nun bildirilen tipi Object → f(Object). JVM: nesne Alt, geçersiz kılınmış sürüm.
İki sürüm de uyar; en özel olan f(String) seçilir. Alt onu geçersiz kılmadığı için Ust’unki çalışır.
Ust’ta f(Integer) yok: 5 kutulanıp Object’e uyar. Seçilen imza f(Object); JVM’de Alt’ın sürümü.
Aynı nesne, ama referans tipi Alt: f(Integer) artık görünür ve f(Object)’ten daha özeldir.
o’nun gösterdiği nesne Integer olsa da bildirilen tip Object: yalnızca f(Object) uyar.
// p(s): System.out.println(s) class Ust { void f(Object o) { p("Ust.f(Object)"); } void f(String s) { p("Ust.f(String)"); } } class Alt extends Ust { @Override void f(Object o) { p("Alt.f(Object)"); } void f(Integer i) { p("Alt.f(Integer)"); } } // main içinde: Ust u = new Alt(); Alt a = (Alt) u; Object o = 5; u.f(o); u.f("x"); u.f(5); a.f(5); a.f(o);
Çıktı Sorusu · Kalıtım ve Çok Biçimlilik
Soru 13: Şekiller Dizisi
Sekil soyut; ciz() sınıf adını yazar, yalnızca Dikdortgen onu geçersiz kılıyor. Program ne yazar?
abstract class Sekil { abstract double alan(); void ciz() { String ad = getClass().getSimpleName(); System.out.println(ad + " çiziliyor"); } } // alan(): Kare(k) → k*k, Dikdortgen(a, b) → a*b, // DikUcgen(a, b) → a*b/2 // Dikdortgen'in ciz()'i şunu yazar: // "Dikdörtgen: " + a + "x" + b static double toplamAlan(Sekil[] dizi) { double toplam = 0; for (Sekil s : dizi) { toplam += s.alan(); s.ciz(); } return toplam; } // main içinde: Sekil[] d = { new Kare(3), new Dikdortgen(2, 5), new DikUcgen(3, 4) }; System.out.println(toplamAlan(d));
Cevabı göster
Kare çiziliyor Dikdörtgen: 2.0x5.0 DikUcgen çiziliyor 25.0
- Her turda
s.alan()nesnenin kendi formülünü çalıştırır: 9.0 + 10.0 + 6.0 = 25.0. KareveDikUcgenciz()’i geçersiz kılmadı:Sekil’in sürümü çalışır;getClass()yine de nesnenin gerçek sınıfını verir.- Yeni bir şekil eklemek için
toplamAlanhiç değişmez.
Çıktı Sorusu · Kalıtım ve Çok Biçimlilik
Soru 14: equals(Kisi) Tuzağı
Kisi sınıfında equals(Kisi) yazılmış. Program ne yazar?
class Kisi { String ad; Kisi(String ad) { this.ad = ad; } public boolean equals(Kisi k) { return ad.equals(k.ad); } } // main içinde: Kisi a = new Kisi("Ali"); Kisi b = new Kisi("Ali"); Object o = b; System.out.println(a.equals(b)); System.out.println(a.equals(o)); System.out.println(o.equals(a));
Cevabı göster
true false false
equals(Kisi k),Object’tekiequals(Object)’i geçersiz kılmaz, aşırı yükler.a.equals(b): argümanKisi→ yeni metot →true.a.equals(o): argümanın bildirilen tipiObject→Object.equalsseçilir; referans karşılaştırması →false.o.equals(a):Objecttipinde yalnızcaequals(Object)görünür; geçersiz kılınmadığı için yinefalse.@Overrideyazılsaydı hata derleme zamanında yakalanırdı.
Kod Yazma · Kalıtım ve Çok Biçimlilik
Soru 15: Ödeme Türleri
Odeme sınıfı (protected double tutar, odenecek() = tutar), ondan türeyen KrediKarti (%2 komisyon: tutar × 1.02) ve KrediKarti’ndan türeyen Taksitli (kartın tutarına ek olarak taksit başına 5 TL) sınıflarını yazın. { new Odeme(100), new KrediKarti(200), new Taksitli(300, 3) } dizisinin toplam ödenecek tutarı nedir?
Cevabı göster
class KrediKarti extends Odeme {
KrediKarti(double tutar) { super(tutar); }
@Override double odenecek() { return tutar * 1.02; }
}
class Taksitli extends KrediKarti {
int taksit;
Taksitli(double tutar, int taksit) {
super(tutar); this.taksit = taksit;
}
@Override double odenecek() {
return super.odenecek() + 5 * taksit;
}
}
// toplam += o.odenecek(); (Odeme o : liste)100.0 + 204.0 + (306.0 + 15) = 625.0. Döngü yalnızca Odeme tipini bilir; super.odenecek() komisyon kuralını tekrar yazmadan kullanır.
Çoktan Seçmeli · Kalıtım ve Çok Biçimlilik
Soru 16: Neden Çok Biçimlilik?
Toplam alanı instanceof zinciriyle (if (s instanceof Kare) … else if …) hesaplamak yerine s.alanHesapla() çağırmanın asıl yararı nedir?
B: Çağrı üst sınıf tipiyle bir kez yazılır; hangi sürümün çalışacağına nesne karar verir. Zincirde yeni tür unutulursa sessizce yanlış sonuç (ör. 0) çıkar. Hız asıl amaç değildir (A); derleyici denetimi sürer (C); soyut metodu her somut alt sınıf yine yazmalıdır (D).