Bölüm 4: Çok Biçimlilik

Bölüm 4: Çok Biçimlilik · Çalışma Soruları
Referans kuralıYukarı / aşağı dönüşüminstanceofDinamik bağlamaStatik bağlamaAşırı yüklemeŞekil dizisiequals tuzağı
Sercan KÜLCÜ

Ç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
1DerlemeSekil → Kare için açık dönüşüm gerekir
2SorunsuzNesne gerçekten bir Kare
3DerlemeSekil’de kenar() yok
4DerlemeObject’te alan() yok
5Sorunsuz4.0 yazar (dinamik bağlama)
6ÇalışmaClassCastException: 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?

YığınHeapmain()oh?k?#1 Kopek

Tek bir Kopek nesnesi; Object tipindeki o onu gösterir (yukarı dönüşüm, otomatik).

YığınHeapmain()ohk?#1 Kopek

(Hayvan) o aşağı dönüşümdür: JVM nesnenin bir Hayvan olduğunu denetler. Yeni nesne oluşmaz.

YığınHeapmain()ohk?#1 Kopek

Referans tipi Hayvan olsa da nesne Kopek: Kopek.sesCikar() çalışır.

YığınHeapmain()ohk#1 Kopek

(Kopek) h başarılı; havla() artık çağrılabilir, çünkü referans tipi Kopek.

YığınHeapmain()ohk#1 Kopek

Üç referans, tek nesne: o == k → true. Dönüşüm nesneyi değil, bakışı değiştirir.

YığınHeapmain()ohk#1 Kopek

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?

YığınHeapAlt()thismain()#1 Altx0

Nesne ayrılır, x varsayılan 0. Alt()’ın ilk işi gizli super(): x = 5 ilk değeri henüz çalışmadı.

YığınHeapUst()thisAlt()thismain()#1 Altx0

Ust() gövdesi çalışır: Ust.

YığınHeapAlt.yaz()thisUst()thisAlt()thismain()#1 Altx0

yaz() = this.yaz(); nesne bir Alt, dinamik bağlama Alt.yaz()’ı seçer. x hâlâ 0: Alt.yaz x=0.

YığınHeapAlt()thismain()#1 Altx5

super() döndükten sonra Alt’ın alan ilk değerleri çalışır: x = 5.

YığınHeapAlt()thismain()#1 Altx5

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(): nesne R → R.a; super.a() P.a’yı çalıştırır (Q’da a yok).
  • P.a içindeki b() = this.b(); this bir R, en yakın b() Q’da: Q.b.
  • new Q().a(): a P’den gelir, b Q’nunkidir. Düz P nesnesinde ikisi de P’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?

YığınHeapmain()uao#1 Alt#2 Integerdeğer5

Tek bir Alt nesnesi; u onu Ust, a Alt olarak görür. o kutulanmış bir Integer’ı gösterir.

u.f(o)1) Derleyici: referans tipi Ust, argüman Objectf(Object)✓ seçilirf(String)uymaz2) JVM: nesne Altçalışan: Alt.f(Object)

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.

u.f("x")1) Derleyici: referans tipi Ust, argüman Stringf(Object)uyarf(String)✓ seçilir2) JVM: nesne Altçalışan: Ust.f(String)

İ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.

u.f(5)1) Derleyici: referans tipi Ust, argüman intf(Object)✓ seçilirf(String)uymaz2) JVM: nesne Altçalışan: Alt.f(Object)

Ust’ta f(Integer) yok: 5 kutulanıp Object’e uyar. Seçilen imza f(Object); JVM’de Alt’ın sürümü.

a.f(5)1) Derleyici: referans tipi Alt, argüman intf(Object)uyarf(String)uymazf(Integer)✓ seçilir2) JVM: nesne Altçalışan: Alt.f(Integer)

Aynı nesne, ama referans tipi Alt: f(Integer) artık görünür ve f(Object)’ten daha özeldir.

a.f(o)1) Derleyici: referans tipi Alt, argüman Objectf(Object)✓ seçilirf(String)uymazf(Integer)uymaz2) JVM: nesne Altçalışan: Alt.f(Object)

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.
  • Kare ve DikUcgen ciz()’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 toplamAlan hiç 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’teki equals(Object)’i geçersiz kılmaz, aşırı yükler.
  • a.equals(b): argüman Kisi → yeni metot → true.
  • a.equals(o): argümanın bildirilen tipi Object → Object.equals seçilir; referans karşılaştırması → false.
  • o.equals(a): Object tipinde yalnızca equals(Object) görünür; geçersiz kılınmadığı için yine false. @Override yazı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).

Son

Bölüm 4: Çok Biçimlilik: Çalışma Soruları
Konu anlatımı: çok biçimlilik sunumu · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.