Bölüm 4.3: Kalıtım

Java ile Nesne Yönelimli Programlama
Üst ve alt sınıfextendssuper ve yapıcı zinciriGeçersiz kılma ve @Override protectedfinalis-a ve has-aKalıtım mı, oluşum mu?Çoklu kalıtımObject sınıfıtoString, equals, hashCode
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

Kavram

Kalıtımı (inheritance), üst sınıf ve alt sınıf kavramlarını tanımlamak; “is-a” ilişkisini tanımak.

Söz dizimi

extends ile alt sınıf yazmak; neyin kalıtımla alındığını, neyin alınmadığını açıklamak.

Yapıcılar

super(...) ve super.metot() kullanmak; yapıcı zincirinin çalışma sırasını izlemek.

Geçersiz kılma

Metodu geçersiz kılmak (overriding), aşırı yüklemeden (overloading) ayırmak, @Override kullanmak.

Erişim ve sınırlama

protected erişimini ve final ile kalıtımın nasıl sınırlandığını açıklamak.

Object sınıfı

toString, equals ve hashCode metotlarını doğru yazmak; == ile equals farkını bilmek.

Kalıtım Kavramı

Kalıtım Nedir?

  • Kalıtım (Inheritance): mevcut bir sınıfın alan ve metotlarını kalıtımla alıp yeni veya değiştirilmiş yeteneklerle zenginleştirerek yeni bir sınıf oluşturmaktır.
  • Mevcut sınıf: üst sınıf (superclass).
  • Yeni sınıf: alt sınıf (subclass).
  • Alt sınıfa yeni alan ve metotlar eklenebilir; kalıtımla alınan alanlar yeniden tanımlanmaz.
Benzetme: “Her dikdörtgen bir şekildir.” Dikdörtgen, şeklin her şeyine sahiptir; üstüne kendi en, boy ve alan() bilgisini ekler.
üst sınıf (superclass)Sekil- renk : String+ ciz() : voidextendsDikdortgen- en : double- boy : double+ alan() : doublealt sınıf (subclass)

Kalıtım Kavramı

extends ile Alt Sınıf

// Temel sınıf (üst sınıf)
public class Sekil {
    // Temel sınıfa ait nitelik ve metotlar
    // ...
}

// Yeni sınıf (alt sınıf), Sekil'den türer
public class Dikdortgen extends Sekil {
    // Yeni sınıfa ait nitelik ve metotlar
    // ...
}
  • Söz dizimi: class AltSinif extends UstSinif. extends: “genişletir”.
  • extends sonrasına yalnızca bir sınıf yazılabilir.
  • Alt sınıf, üst sınıfın public ve protected üyelerini kendi üyesiymiş gibi kullanır.
  • Üst sınıftaki aynı adlı alanı alt sınıfta yeniden tanımlamak onu gizler (hiding); iki ayrı değişken oluşur ve kafa karıştırır. Yapılmamalıdır.
  • extends yazılmazsa sınıf, gizlice Object sınıfından türer.
Dikdortgenalt sınıfSekilüst sınıfObjectgizli üst sınıfextendsextends

Kalıtım Kavramı

Alt ve Üst Sınıf

  • Alt sınıf, üst sınıfın davranışlarını sergiler; ek olarak kendine özgü davranışlar da sergiler.
  • Kalıtım bu yüzden özelleştirme (specialization) olarak da adlandırılır: alt sınıf daha özel bir grubu temsil eder.
  • Her kedi bir hayvandır, ama her hayvan kedi değildir.
  • Java sınıf hiyerarşisi, java.lang paketindeki Object sınıfı ile başlar.
  • Her sınıf, doğrudan veya dolaylı olarak Object sınıfından kalıtımla alır.
ObjectHayvanStringSekilKediKopekDikdortgenyukarı: genel · aşağı: özel

Kalıtım Kavramı

Kalıtım Türleri

  • Tek düzeyli (single): bir alt sınıf, bir üst sınıf. Kedi extends Hayvan.
  • Çok düzeyli (multilevel): zincir biçiminde. Kedi, Canli'nin üyelerini Hayvan üzerinden alır; her düzey bir öncekinin her şeyini taşır.
  • Hiyerarşik (hierarchical): bir üst sınıftan birçok alt sınıf. Ortak kod bir kez yazılır, her alt sınıf yalnızca farkını ekler.
  • Çoklu (multiple): bir sınıfın birden çok üst sınıfı. Java sınıflarda buna izin vermez; aynı ihtiyaç arayüzlerle karşılanır.
Java'da sınıf hiyerarşisi bir ağaçtır: kökü Object'tir ve Object dışındaki her sınıfın tam olarak bir doğrudan üst sınıfı vardır. Zincirin derinliği sınırsızdır, ama derin zincirler okunmayı ve değiştirmeyi zorlaştırır.
Tek düzeyliÇok düzeyliHiyerarşikÇoklu (sınıfta yok)HayvanKediCanliHayvanKediHayvanKediKopekKusBCD✗arayüzlerle sağlanır

Kalıtım Kavramı

Neler Kalıtımla Alınır?

ÜyeAlt sınıfa geçer mi?Not
public / protected alan ve metotlarEvetAlt sınıfta doğrudan kullanılır.
Belirteçsiz (paket erişimli) üyelerYalnızca aynı paketteyseBaşka paketteki alt sınıf erişemez.
private alanlarNesnede yer alır, ama doğrudan erişilemezÜst sınıfın public/protected metotlarıyla (getter, setter) kullanılır.
private metotlarHayırGörünmez; geçersiz kılınamaz.
Yapıcılar (constructors)HayırKalıtımla alınmaz; alt sınıf yapıcısı super(...) ile çağırır.
static üyelerErişilebilirSınıfa aittir; geçersiz kılınmaz, yalnızca gizlenebilir.

Kısaca: alt sınıf nesnesinin içinde üst sınıfın bütün alanları vardır; hangisine doğrudan erişilebileceğini erişim belirteci belirler.

Kalıtım Kavramı

Alt Sınıf Nesnesinin Yapısı

  • new Kedi(...) tek bir nesne oluşturur; üst sınıf için ayrı bir nesne oluşmaz.
  • Nesnede üst sınıftan gelen alanlar için de yer ayrılır. private olsalar bile oradadırlar; yalnızca Kedi'nin kodu onlara adıyla erişemez.
  • super ayrı bir nesne değildir: aynı nesnenin üst sınıf kısmına, üst sınıfın kodu üzerinden ulaşmanın yoludur.
Yığınmain()kHeap: tek bir nesneKedi nesnesiObject kısmıHayvan kısmı- isim = "Mavi"- saglik = 100.0Kedi kısmı- evcil = truethis ve super aynı nesnedir

Kanıt: yapıcıdaki getClass()

class Hayvan {
    Hayvan() {
        System.out.println("Hayvan() içinde: "
            + getClass().getSimpleName());
    }
}
class Kedi extends Hayvan { }
// main içinde:
new Kedi();
new Hayvan();
Hayvan() içinde: Kedi
Hayvan() içinde: Hayvan

Hayvan yapıcısı çalışırken bile nesnenin gerçek sınıfı Kedi'dir: yapıcı zinciri, aynı nesnenin kısımlarını sırayla kurar.

Kalıtım Kavramı

Ortak Kodu Üst Sınıfa Taşımak

Genelleştirme (generalization): birkaç sınıfta tekrar eden alan ve metotları bulup ortak bir üst sınıfa taşımak. Özelleştirmenin ters yönüdür.

Önce: aynı kod iki yerde

class Ogrenci {
    private String ad;
    private int yas;
    private double ortalama;
    String kimlik() { return ad + " (" + yas + ")"; }
}
class Ogretmen {
    private String ad;
    private int yas;
    private String brans;
    String kimlik() { return ad + " (" + yas + ")"; }
}

Yaş biçimi değişirse iki sınıf da düzeltilmeli; biri unutulursa çıktılar tutarsız olur.

Sonra: ortak kısım Kisi'de

class Kisi {
    private String ad;
    private int yas;
    Kisi(String ad, int yas) { this.ad = ad; this.yas = yas; }
    String kimlik() { return ad + " (" + yas + ")"; }
}
class Ogrenci extends Kisi {
    private double ortalama;
    Ogrenci(String ad, int yas, double ortalama) {
        super(ad, yas);
        this.ortalama = ortalama;
    }
    @Override String kimlik() {
        return super.kimlik() + ", ort. " + ortalama;
    }
}   // Ogretmen de aynı biçimde: brans eklenir
Ece (20), ort. 3.4
Murat (41), Fizik

Taşımadan önce sorulacak soru: “Her öğrenci bir kişi midir?” Evet ise genelleştirme doğrudur; yalnızca iki sınıfın birkaç alanı benziyorsa oluşum daha uygundur.

Hayvan Örneği

Hayvan Sınıf Hiyerarşisi

Hayvan- isim : String- yas : int- saglik : double+ getIsim() : String+ konus() : void+ yemekYe(double) : void+ uyu() : void+ toString() : StringKedi+ konus() : void+ avlan() : voidKopek+ konus() : void+ topOyna() : voidKus+ konus() : void+ ucmak() : voidAt+ konus() : void+ kos() : void

Ortak alan ve metotlar bir kez, Hayvan sınıfında yazılır. Her alt sınıf konus() metodunu kendine göre geçersiz kılar ve kendine özgü bir metot ekler. UML'de kalıtım, ucu üst sınıfa dönük içi boş üçgen okla gösterilir.

Hayvan Örneği

Hayvan ve Kopek Sınıfları

public class Hayvan {
    private String isim;     // ortak nitelikler
    private int yas;
    private double saglik;

    public Hayvan(String isim, int yas) {
        this.isim = isim;
        this.yas = yas;
        this.saglik = 100.0;
    }
    public String getIsim() { return isim; }   // private isim'e erişim yolu
    public void konus() {
        System.out.println("Bu hayvan konuşur.");
    }
    public void uyu() {
        saglik += 5;
        System.out.println(isim + " uyudu. Sağlık: " + saglik);
    }
}

Üst sınıf Hayvan: bütün hayvanlarda ortak olan alanlar ve davranışlar.

public class Kopek extends Hayvan {
    public Kopek(String isim, int yas) {
        super(isim, yas);   // üst sınıf yapıcısı alanları doldurur
    }
    @Override
    public void konus() {   // miras alınan davranışı değiştirir
        System.out.println(getIsim() + " havlar.");
    }
    public void topOyna() { // yalnızca Kopek'te olan davranış
        System.out.println(getIsim() + " top oynuyor!");
    }
}
Kopek kopek = new Kopek("Karabaş", 4);
kopek.konus();     // Kopek'in sürümü
kopek.topOyna();   // Kopek'e özgü
kopek.uyu();       // Hayvan'dan miras
Karabaş havlar.
Karabaş top oynuyor!
Karabaş uyudu. Sağlık: 105.0

Alt sınıf Kopek yalnızca farkı yazar; uyu() hiç yazılmadığı hâlde çalışır.

Hayvan Örneği

Kalıtımın Avantajları

Yeniden kullanım

Üst sınıfın metot ve alanları alt sınıfta yeniden yazılmadan kullanılır.

Genişletme

Mevcut sınıf, kodu değiştirilmeden yeni özelliklerle genişletilir.

Değiştirme

Üst sınıfın metotları alt sınıfta geçersiz kılınarak (overriding) değiştirilebilir.

HesapMakinesi- hafiza : double+ topla(int, int) : double+ carp(int, int) : double+ hafizayiGoruntule() : doubleGelismisHesapMakinesi+ us(double, int) : double+ faktoriyel(int) : long
public class GelismisHesapMakinesi
        extends HesapMakinesi {
    // Yeni yetenek: üs alma
    public double us(double taban, int us) {
        double sonuc = 1.0;
        for (int i = 0; i < us; i++) {
            sonuc *= taban;
        }
        return sonuc;
    }
    // faktoriyel(int) benzer biçimde eklenir
}
// main içinde
GelismisHesapMakinesi h = new GelismisHesapMakinesi();
System.out.println(h.topla(5, 3));         // 8.0  (miras)
System.out.println(h.hafizayiGoruntule()); // 8.0  (miras)
System.out.println(h.us(2, 5));            // 32.0 (yeni)

Yapıcılar ve super

super Anahtar Sözcüğü

  • super(...): alt sınıfın yapıcısı içinde üst sınıfın yapıcısını çağırır; üst sınıfın alanlarına ilk değerleri o atar.
  • super(...) yapıcının ilk satırı olmalıdır (Java 25'ten önce; Java 25 önüne nesneye dokunmayan deyimlere izin verir).
  • Yazılmazsa derleyici gizlice super(); (parametresiz) ekler.
  • super.metot(): üst sınıfın metoduna nokta (.) ile erişir; geçersiz kılınmış bir metodun içinden üst sınıfın sürümünü çağırmak için kullanılır.
  • İlke: her sınıf yalnızca kendi verilerini yönetmelidir. Kedi, Hayvan'ın isim alanına kendisi atama yapmaz; işi super(isim)'e bırakır.
class Kedi extends Hayvan {
    private boolean evcil;

    Kedi(String isim, boolean evcil) {
        super(isim);         // Hayvan(String)
        this.evcil = evcil;  // kendi verisi
    }

    @Override
    public String toString() {
        return super.toString()   // Hayvan'ınki
               + ", Evcil: " + evcil;
    }
}
// main içinde
System.out.println(new Kedi("Mavi", true));

Çıktı

İsim: Mavi, Sağlık: 100.0, Evcil: true

Hayvan'ın toString() metodu isim ve sağlık bilgisini döndürür; Kedi yalnızca kendi alanını ekler.

Yapıcılar ve super · Adım adım kod izleme

super.tanit() Zinciri: Kod İzleme

Canlitanit()Hayvantanit()Keditanit()Çağrı yığınıKedi.tanit()main()nesne: Kedi #1arama gerçek sınıftan başlar: Kedi.tanit()

Nesne bir Kedi; arama en alttan başlar ve Kedi.tanit() bulunur.

Canlitanit()Hayvantanit()Keditanit()superÇağrı yığınıHayvan.tanit()Kedi.tanit()main()nesne: Kedi #1

super.tanit(): bir üst sınıfın sürümü çağrılır. Kedi.tanit bekler, yığında kalır.

Canlitanit()Hayvantanit()Keditanit()supersuperÇağrı yığınıCanli.tanit()Hayvan.tanit()Kedi.tanit()main()nesne: Kedi #1

Nesne hâlâ bir Kedi olsa da Hayvan içindeki super sabit olarak Canli'yi gösterir: dinamik bağlama yok.

Canlitanit()Hayvantanit()Keditanit()Çağrı yığınıCanli.tanit()Hayvan.tanit()Kedi.tanit()main()nesne: Kedi #1Canlıyım yazılır, Canli.tanit() döner

En üstteki sürüm çalışır ve ilk satırı yazar; sonra döner.

Canlitanit()Hayvantanit()Keditanit()Çağrı yığınıHayvan.tanit()Kedi.tanit()main()nesne: Kedi #1

Hayvan.tanit() kaldığı yerden sürer: Hayvanım.

Canlitanit()Hayvantanit()Keditanit()Çağrı yığınıKedi.tanit()main()nesne: Kedi #1

Son olarak Kedi.tanit() kendi satırını yazar.

Canlitanit()Hayvantanit()Keditanit()Çağrı yığınımain()nesne: Kedi #1çağrı aşağıdan yukarı, çıktı yukarıdan aşağı

Çağrılar Kedi → Hayvan → Canli yönünde gitti, çıktılar ters sırada geldi: her sınıf önce üst sınıfın işini yaptırıp sonra kendi farkını ekledi.

class Canli {
    void tanit() { System.out.println("Canlıyım"); }
}
class Hayvan extends Canli {
    void tanit() {
        super.tanit();
        System.out.println("Hayvanım");
    }
}
class Kedi extends Hayvan {
    void tanit() {
        super.tanit();
        System.out.println("Kediyim");
    }
}
new Kedi().tanit();   // main

Yapıcılar ve super · Adım adım kod izleme

Yapıcı Zinciri: new Kedi("Mavi", true)

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = nullsaglik = 0.0evcil = false

new: önce bellekte nesneye yer ayrılır; tüm alanlar (Hayvan'ınkiler dahil) varsayılan değerleri alır.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = nullsaglik = 0.0evcil = false

Kedi yapıcısı çağrılır. Kendi işine başlamadan önce üst sınıf kısmı hazırlanmalıdır.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = nullsaglik = 0.0evcil = false

super(isim): Hayvan yapıcısı çağrılır; Kedi yapıcısı bekler.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = nullsaglik = 0.0evcil = false

Hayvan da önce kendi üst sınıfını çağırır: Object(). Her zincir Object'te biter.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 0.0evcil = false

Object() döndü. Hayvan kendi alanını doldurur: isim = "Mavi".

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = false

saglik = 100.0.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = false

Hayvan yapıcısı mesajını yazar ve döner: önce üst sınıf yazdı.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = true

Kedi yapıcısı kaldığı yerden devam eder: evcil = true.

kedi—Kedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = true

Kedi yapıcısı mesajını yazar.

kediKedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = true

Yapıcı bitti; nesnenin adresi kedi değişkenine atanır.

kediKedi nesnesiHayvan'dan gelenKedi'nin kendiisim = "Mavi"saglik = 100.0evcil = true

Yapıcılar yukarıdan aşağıya tamamlanır: Object → Hayvan → Kedi.

class Hayvan {
    private String isim;
    private double saglik;
    Hayvan(String isim) {
        super();   // Object() çağrılır
        this.isim = isim;
        saglik = 100.0;
        System.out.println("Hayvan yapıcısı");
    }
}
class Kedi extends Hayvan {
    private boolean evcil;
    Kedi(String isim, boolean evcil) {
        super(isim);
        this.evcil = evcil;
        System.out.println("Kedi yapıcısı");
    }
}
// main içinde
Kedi kedi = new Kedi("Mavi", true);
System.out.println("Nesne hazır");

Yapıcılar ve super

Gizli super() ve Hatalar

1) Üst sınıfta parametresiz yapıcı yok

class Hayvan {
    private String isim;
    Hayvan(String isim) { this.isim = isim; }
}
class Kedi extends Hayvan {
    Kedi(String isim) {
        // derleyici buraya super(); ekler
        System.out.println("Kedi");
    }
}
error: constructor Hayvan in class Hayvan
  cannot be applied to given types;
  required: String
  found:    no arguments

2) super(...) ilk satırda değil

Kedi(String isim) {
    System.out.println("Kedi");
    super(isim);
}
error: call to super must be first
  statement in constructor
Çözüm: alt sınıf yapıcısının ilk satırında uygun üst sınıf yapıcısı açıkça çağrılır: super(isim);

Hiç yapıcı yazılmamış sınıfa derleyici parametresiz bir varsayılan yapıcı (default constructor) ekler; bir yapıcı yazıldığı anda bu eklenmez.

Kedi(String isim)super(…) yazılmadısuper();derleyici eklerHayvan() ?yok: yalnızca Hayvan(String)çözüm: ilk satıra super(isim);

Yapıcılar ve super · Adım adım kod izleme

this(...) ve super(...): Kod İzleme

Yığın (stack)HeapYonetici(ad)main()y?Yonetici #1adnullmaas0.0prim0.0Calisan kısmı: ad, maasnew: nesne ayrıldı, alanlar varsayılan

new önce nesneye yer ayırır; üç alan da varsayılan değerdedir. Ardından Yonetici(String) çağrılır.

Yığın (stack)HeapYonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1adnullmaas0.0prim0.0Calisan kısmı: ad, maasthis(...) → aynı sınıfın öteki yapıcısı

İlk satır this(ad, 5000.0): aynı sınıfın iki parametreli yapıcısına geçilir. Yonetici(String) gövdesini henüz çalıştırmadan bekler.

Yığın (stack)HeapCalisan(ad)Yonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1adnullmaas0.0prim0.0Calisan kısmı: ad, maassuper(ad) → üst sınıfın yapıcısı

super(ad): üst sınıf Calisan'ın tek parametreli yapıcısı. Yönetici kısmının kurulması, Calisan kısmı bitene kadar bekler.

Yığın (stack)HeapCalisan(ad,maas)Calisan(ad)Yonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1adnullmaas0.0prim0.0Calisan kısmı: ad, maasthis(...) yine; zincirin sonu gizli super()

Calisan da this(...) ile devreder. Bu yapıcının ilk satırı this ya da super olmadığı için derleyici gizli super() ekler: Object() çalışıp döner.

Yığın (stack)HeapCalisan(ad,maas)Calisan(ad)Yonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1ad"Ayşe"maas20000.0prim0.0Calisan kısmı: ad, maasCalisan kısmı kuruldu

İlk gövde burada çalışır: ad ve maas dolar, ilk satır yazılır. Çağrılar aşağı doğru gitmişti, gövdeler en üstteki sınıftan başlayarak tamamlanır.

Yığın (stack)HeapCalisan(ad)Yonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1ad"Ayşe"maas20000.0prim0.0Calisan kısmı: ad, maasCalisan(String) kaldığı yerden sürer

İki parametreli yapıcı döndü; Calisan(String) kendi satırını yazar.

Yığın (stack)HeapYonetici(ad,prim)Yonetici(ad)main()y?Yonetici #1ad"Ayşe"maas20000.0prim5000.0Calisan kısmı: ad, maasYonetici kısmı kuruluyor

super(ad) tamamlandı. Ancak şimdi alt sınıfın kendi alanı dolar: prim = 5000.0.

Yığın (stack)HeapYonetici(ad)main()y?Yonetici #1ad"Ayşe"maas20000.0prim5000.0Calisan kısmı: ad, maasilk çağrılan yapıcı en son biter

En son, ilk çağrılan Yonetici(String) biter. Dört yapıcı çalıştı ama tek bir nesne kuruldu.

Yığın (stack)Heapmain()yYonetici #1ad"Ayşe"maas20000.0prim5000.0Calisan kısmı: ad, maasgelir() = 20000.0 + 5000.0

Nesnenin adresi y'ye atanır. gelir(), kalıtımla gelen getMaas() ile kendi prim alanını toplar: 25000.0.

class Calisan {
    private String ad;  private double maas;
    Calisan(String ad) {
        this(ad, 20000.0);
        System.out.println("Calisan(String)");
    }
    Calisan(String ad, double maas) {
        this.ad = ad;  this.maas = maas;
        System.out.println("Calisan(String, double)");
    }
    double getMaas() { return maas; }
}
class Yonetici extends Calisan {
    private double prim;
    Yonetici(String ad) {
        this(ad, 5000.0);
        System.out.println("Yonetici(String)");
    }
    Yonetici(String ad, double prim) {
        super(ad);
        this.prim = prim;
        System.out.println("Yonetici(String, double)");
    }
    double gelir() { return getMaas() + prim; }
}
Yonetici y = new Yonetici("Ayşe");   // main
System.out.println(y.gelir());

Yapıcılar ve super · Adım adım kod izleme

Kalıtımda İlklendirme Sırası: Kod İzleme

Sınıflar (bir kez)Uststatic:çalışıyorAltstatic:bekliyorNesneler1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()Ust yükleniyor: önce üst sınıf

İlk new Alt() için JVM Alt sınıfını başlatmalıdır. Önce üst sınıf başlatılır: Ust'un static bloğu çalışır.

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalışıyorNesneler1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()Alt yükleniyor

Sonra Alt'ın static bloğu. Sınıf başlatma program boyunca bir kez yapılır.

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalıştı ✓NesnelerAlt #1a1b0(a: Ust, b: Alt)1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()Alt() → gizli super() → Ust alanları

Nesne ayrıldı (a ve b 0). Alt() önce gizli super() der; Ust() gövdesinden önce Ust'un alan ilk değerleri çalışır: a = deger(...).

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalıştı ✓NesnelerAlt #1a1b0(a: Ust, b: Alt)1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()Ust() gövdesi

Ardından Ust() gövdesi. Bu anda b hâlâ 0: Alt kısmı henüz hiç ilk değer almadı.

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalıştı ✓NesnelerAlt #1a1b1(a: Ust, b: Alt)1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()super() döndü → Alt alanları

super() döndükten sonra alt sınıfın alan ilk değerleri çalışır: b = deger("Alt alanı").

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalıştı ✓NesnelerAlt #1a1b1(a: Ust, b: Alt)1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()Alt() gövdesi: nesne hazır

En son Alt() gövdesi. Sıra: üst sınıf alanları → üst yapıcı → alt sınıf alanları → alt yapıcı.

Sınıflar (bir kez)Uststatic:çalıştı ✓Altstatic:çalıştı ✓NesnelerAlt #1a1b1(a: Ust, b: Alt)Alt #2a1b1(a: Ust, b: Alt)1. Ust static2. Alt static3. Ust alanları4. Ust()5. Alt alanları6. Alt()ikinci nesne: static bloklar yok

İkinci new Alt(): sınıflar zaten başlatıldı, static bloklar tekrar çalışmaz. Nesneye ait dört adım aynı sırayla yinelenir.

class Ust {
    static { System.out.println("Ust static"); }
    int a = deger("Ust alanı");
    Ust() { System.out.print("Ust(), "); }
    static int deger(String s) {
        System.out.print(s + ", ");
        return 1;
    }
}
class Alt extends Ust {
    static { System.out.println("Alt static"); }
    int b = deger("Alt alanı");
    Alt() { System.out.println("Alt()"); }
}
// main içinde:
new Alt();
new Alt();

Geçersiz Kılma

Metodu Geçersiz Kılma

Geçersiz kılma (overriding): alt sınıfın, üst sınıftan kalıtımla aldığı bir metodu aynı imzayla yeniden yazarak kendi davranışını vermesidir.

  • Ad ve parametre listesi aynı olmalı.
  • Dönüş tipi aynı (veya onun alt tipi) olmalı.
  • Erişim daraltılamaz: public metot public kalmalı.
  • final, static ve private metotlar geçersiz kılınamaz.
class Sekil {
    public void ciz() {
        System.out.println("Şekil çiziliyor.");
    }
}
class Dikdortgen extends Sekil {
    @Override
    public void ciz() {
        System.out.println("Dikdörtgen çiziliyor.");
    }
}
// main içinde
new Sekil().ciz();
new Dikdortgen().ciz();
Şekil çiziliyor.
Dikdörtgen çiziliyor.
// alt sınıfta: void ciz()
error: ciz() in Dikdortgen cannot override
  ciz() in Sekil
  attempting to assign weaker access
  privileges; was public
// alt sınıfta: public int ciz()
error: ciz() in Dikdortgen cannot override
  ciz() in Sekil
  return type int is not compatible
  with void

Geçersiz Kılma

Geçersiz Kılma ve Aşırı Yükleme

Geçersiz kılma (overriding)Aşırı yükleme (overloading)
Nerede?Alt sınıfta, kalıtımla alınan metot içinAynı sınıfta (veya alt sınıfta)
AdAynıAynı
ParametrelerAynıFarklı (sayı ya da tip)
Dönüş tipiAynı veya alt tipiSerbest
AmaçKalıtımla alınan davranışı değiştirmekAynı işi farklı girdilerle yapmak
Seçim zamanıÇalışma zamanı (nesnenin gerçek tipi)Derleme zamanı (argüman tipleri)
class Dikdortgen extends Sekil {
    @Override
    public void ciz() { ... }             // geçersiz kılma
    public void ciz(String renk) { ... }  // aşırı yükleme
}

d.ciz("Mavi") çıktısı

Mavi dikdörtgen çiziliyor.

Her iki kavram da çok biçimliliğin (polymorphism) temelidir.

Geçersiz Kılma

@Override Etiketi

  • @Override bir etikettir (annotation): metodun, üst sınıftan kalıtımla alınan bir metodu geçersiz kıldığını bildirir.
  • Derleyiciye bunu kontrol etmesini söyler: üst sınıfta böyle bir metot yoksa derleme hatası verir.
  • Kodu daha okunabilir yapar; diğer geliştiricilere metodun üst sınıftan geldiğini gösterir.
  • Zorunlu değildir, ama her zaman yazılması önerilir: yazım hatalarını hemen yakalar.
public class Dikdortgen extends Sekil {
    // miras alınan metodu geçersiz kıl
    @Override
    public void ciz() {
        System.out.println("Dikdörtgen çiziliyor.");
    }
}

Yazım hatası: Ciz (büyük C)

    @Override
    public void Ciz() { ... }
error: method does not override or implement
  a method from a supertype

@Override olmasaydı Ciz() sessizce yeni bir metot olurdu ve ciz() çağrıları hâlâ Sekil'in sürümünü çalıştırırdı.

Geçersiz Kılma

Geçersiz Kılma Kuralları: Ayrıntılı

ÖzellikAlt sınıftaki metotta izin verilenKural bozulursa javac
Ad ve parametrelerBirebir aynıHata yok: yeni bir metot (aşırı yükleme) olur
Dönüş tipiAynı ya da bir alt tipi (kovaryant)return type String is not compatible with Hayvan
ErişimAynı ya da daha geniş: protected → publicattempting to assign weaker access privileges
Kontrol edilen istisnaAynısı, daha özeli ya da hiçoverridden method does not throw Exception
staticÖrnek metodu static olamaz, static metot örnek metodu olamazoverriding method is static / overridden method is static
final metotGeçersiz kılınamazoverridden method is final
private metotGörünmez; aynı adlı metot ilgisiz yeni bir metotturHata yok (@Override varsa hata)
Ortak gerekçe: alt sınıf nesnesi, üst sınıf nesnesinin beklendiği her yerde kullanılabilmelidir. Üst sınıfın verdiği söz (ne döndüreceği, kime açık olduğu, hangi istisnaları fırlatabileceği) alt sınıfta genişletilebilir ya da korunabilir, ama daraltılamaz.

Geçersiz Kılma

Kovaryant Dönüş Tipi

class Hayvan {
    Hayvan yavrula() { return new Hayvan(); }
    public String toString() { return "Hayvan"; }
}
class Kedi extends Hayvan {
    @Override
    Kedi yavrula() { return new Kedi(); }  // alt tip
    void miyavla() { System.out.println("miyav"); }
    public String toString() { return "Kedi"; }
}
// main içinde:
Kedi k = new Kedi();
k.yavrula().miyavla();   // dönüşüm gerekmez
Hayvan h = k;
System.out.println(h.yavrula());
miyav
Kedi
  • Geçersiz kılan metot, üst sınıftaki dönüş tipinin bir alt tipini döndürebilir: kovaryant dönüş tipi (covariant return type).
  • Kedi referansıyla çağıran, sonucu doğrudan Kedi olarak alır; (Kedi) dönüşümüne gerek kalmaz.
  • Hayvan referansıyla çağıran için söz bozulmaz: bir Kedi de bir Hayvan'dır.
  • Yalnızca referans tiplerinde geçerlidir; int yerine long döndürmek olmaz.

İlgisiz tip: String yavrula()

error: yavrula() in Kedi cannot override
  yavrula() in Hayvan
  return type String is not compatible with Hayvan

Geçersiz Kılma

private Metotlar Geçersiz Kılınmaz

class Ust {
    private void selam() { System.out.println("Ust.selam"); }
    void cagir() { selam(); }
}
class Alt extends Ust {
    void selam() { System.out.println("Alt.selam"); } // yeni
}
// main içinde:
Alt a = new Alt();
a.cagir();
a.selam();
Ust.selam
Alt.selam

Alt'taki metoda @Override eklenirse

error: method does not override or implement
  a method from a supertype
  • private bir metot alt sınıfa görünmez; alt sınıfta aynı adla yazılan metot onunla ilgisiz, yeni bir metottur.
  • Ust.cagir() içindeki selam() çağrısı derleme sırasında Ust'un kendi private metoduna bağlanır. Nesne bir Alt olsa da Ust.selam yazılır.
  • @Override alışkanlığı bu yanılgıyı derleme anında ortaya çıkarır.
Aynı durum static metotlarda da görülür: alt sınıftaki aynı imzalı static metot üst sınıfınkini geçersiz kılmaz, yalnızca gizler (hiding).

Geçersiz Kılma

Örnek: Ürün Hiyerarşisi

class Urun {
    private String ad;
    private double fiyat;
    Urun(String ad, double fiyat) {
        this.ad = ad; this.fiyat = fiyat;
    }
    double kdvOrani() { return 0.20; }
    double satisFiyati() { return fiyat * (1 + kdvOrani()); }
    @Override public String toString() {
        double f = Math.round(satisFiyati() * 100) / 100.0;
        return ad + ": " + f + " TL";
    }
}
class GidaUrunu extends Urun {
    GidaUrunu(String ad, double fiyat) { super(ad, fiyat); }
    @Override double kdvOrani() { return 0.01; }
}
class IndirimliUrun extends Urun {
    private double indirim;
    IndirimliUrun(String ad, double fiyat, double indirim) {
        super(ad, fiyat);
        this.indirim = indirim;
    }
    @Override double satisFiyati() {
        return super.satisFiyati() * (1 - indirim);
    }
    @Override public String toString() {
        return super.toString()
            + " (%" + (int) (indirim * 100) + " indirim)";
    }
}
Urun[] sepet = {new Urun("Kalem", 50),
    new GidaUrunu("Ekmek", 10),
    new IndirimliUrun("Kulaklık", 1000, 0.25)};
for (Urun u : sepet) System.out.println(u);
Kalem: 60.0 TL
Ekmek: 10.1 TL
Kulaklık: 900.0 TL (%25 indirim)
  • GidaUrunu yalnızca kdvOrani()'nı değiştirir; satisFiyati() ve toString() yazılmadığı hâlde %1 ile hesaplanır, çünkü Urun'un içindeki çağrı nesnenin sınıfına göre seçilir.
  • IndirimliUrun üst sınıfın hesabını değiştirmez, genişletir: super.satisFiyati() sonucuna indirimi uygular. 1000 × 1,20 × 0,75 = 900.

Erişim ve Sınırlama

protected Erişim Düzeyi

BelirteçAynı sınıfAynı paketAlt sınıf
(başka paket)
Her yer
private✓✗✗✗
(belirteç yok: paket)✓✓✗✗
protected✓✓✓✗
public✓✓✓✓
  • public: sınıfın üyelerine her yerden erişilebilir. private: yalnızca o sınıfın içinden.
  • protected: ikisinin arasında bir düzeydir. Sınıfın kendisi, alt sınıfları ve aynı paketteki diğer sınıflar erişebilir.
  • Bir sınıfın private alanlarına alt sınıf, yalnızca üst sınıftan kalıtımla alınan public veya protected metotlar aracılığıyla erişebilir.

İyi uygulama: alanlar yine private kalsın; alt sınıfların ihtiyaç duyduğu işlemler protected metotlar olarak açılsın.

Erişim ve Sınırlama

protected: Kod Örneği

package canli;

public class Hayvan {
    protected String isim;  // alt sınıflar
    private double saglik;  // yalnızca Hayvan

    public Hayvan(String isim) {
        this.isim = isim;
        saglik = 100.0;
    }
    public double getSaglik() { return saglik; }
    protected void saglikEkle(double miktar) {
        saglik += miktar;
    }
}
package evcil;     // başka paket
import canli.Hayvan;

public class Kedi extends Hayvan {
    public Kedi(String isim) { super(isim); }
    public void uyu() {
        // protected alan: izin var
        System.out.println(isim + " uyuyor.");
        // saglik += 5;  // private: hata
        saglikEkle(5);   // protected metot
        System.out.println("Sağlık: "
                           + getSaglik());
    }
}

new Kedi("Mavi").uyu(); çıktısı

Mavi uyuyor.
Sağlık: 105.0

Yorum satırı açılırsa

error: saglik has private access in Hayvan

Erişim ve Sınırlama

protected Erişimin İnce Kuralı

package canli;
public class Hayvan {
    protected String isim;
    public Hayvan(String isim) {
        this.isim = isim;
    }
}
package evcil;
import canli.Hayvan;
public class Kedi extends Hayvan {
    public Kedi(String isim) { super(isim); }
    void karsilastir(Kedi k, Hayvan h) {
        System.out.println(isim);    // kendi alanı
        System.out.println(k.isim);  // Kedi üzerinden
        System.out.println(h.isim);  // Hayvan üzerinden
    }
}
evcil/Kedi.java:8: error: isim has protected access in Hayvan
        System.out.println(h.isim);
  • Başka paketteki alt sınıf, protected üyeye yalnızca kendi tipinden (ya da kendi alt tiplerinden) bir referans üzerinden erişebilir: isim ve k.isim geçerlidir.
  • h ise herhangi bir Hayvan'ı, örneğin bir Kopek'i gösterebilir. Kedi'nin başka bir alt sınıfın iç verisine dokunması engellenir.
  • Aynı paketteki sınıflar için bu kısıt yoktur: orada protected, paket erişimini de kapsar.

Erişim ve Sınırlama

final ile Kalıtımı Sınırlamak

  • final anahtar sözcüğü kalıtımı sınırlamak için kullanılabilir.
  • Bir metot final tanımlanırsa hiçbir alt sınıfta geçersiz kılınamaz (override).
  • Bir sınıf final tanımlanırsa ondan hiçbir alt sınıf türetilemez. Örnek: String, Math, Integer.
class Hayvan {
    public final void nefesAl() { ... }
}
class Kedi extends Hayvan {
    public void nefesAl() { ... }
}
error: nefesAl() in Kedi cannot override
  nefesAl() in Hayvan
  overridden method is final
final class Sabit { ... }
class Deneme extends Sabit { }
error: cannot inherit from final Sabit
Neden? Güvenlik ve tutarlılık: String değiştirilemez olmalıdır; bir alt sınıf bu sözü bozamasın.

Değişkenlerde final başka bir anlam taşır: değer bir kez atanır (sabit).

class Denemefinal class Sabitextends ✗Kedi.nefesAl()final nefesAl()geçersiz kılma ✗

Erişim ve Sınırlama

Nesnenin Görünümünü Kısıtlama

  • Bir nesne, üst sınıf tipinde bir değişkene atanabilir (yukarı dönüşüm, upcasting): her Lastik bir Parça'dır.
  • Böylece dışarıya yalnızca üst sınıfa ait metotlar açılır.
  • Nesnenin gerçek sınıfına bakılmaksızın, değişkenin tipi neyi çağırabileceğimizi belirler.
Parca pLastik nesnesiParca kısmı · p ile görünürmarka = "Petlas"seriNo = "XYZ-123"seriNumarasi()Lastik kısmı · p ile görünmezebat = "225/45R17"kislik = truekislikMi()
Parca p = new Lastik("Petlas", "XYZ-123", "225/45R17", true);
System.out.println(p.seriNumarasi());   // izin verilir → XYZ-123
// p.kislikMi()                         // izin verilmez
error: cannot find symbol
  symbol:   method kislikMi()
  location: variable p of type Parca

Lastik'e özgü metoda ulaşmak için önce tip kontrolü, sonra aşağı dönüşüm (downcasting) gerekir (instanceof).

Sınıflar Arası İlişkiler

is-a ve has-a İlişkileri

Is-a (kalıtım, inheritance)

  • Bir sınıfın başka bir sınıftan türetilmesidir; türetilen sınıf üst sınıfın bir türüdür.
  • Alt sınıf nesnesi, üst sınıfın nesnesi gibi de kullanılabilir.

Has-a (oluşum, composition)

  • Bir sınıfın başka bir sınıf tipinde alan içermesidir.
  • Nesne, içinde diğer nesnelere referanslar tutar.
Arac# hiz : int+ hizlan(int) : voidAraba- motor : Motor+ yolaCik() : voidMotor- beygirGucu : int+ calistir() : voidis-ahas-a
// Is-a ilişkisi (kalıtım)
public class Araba extends Arac {
    // Has-a ilişkisi (oluşum)
    private Motor motor;

    public Araba(int beygirGucu) {
        motor = new Motor(beygirGucu);
    }
    public void yolaCik() {
        motor.calistir();   // Motor'a devret
        hizlan(50);         // Arac'tan miras
        System.out.println("Hız: " + hiz
                           + " km/sa");
    }
}
// Motor: beygirGucu alanı ve calistir():
//   "Motor çalıştı: " + beygirGucu + " BG"

new Araba(110).yolaCik();

Motor çalıştı: 110 BG
Hız: 50 km/sa

Sınıflar Arası İlişkiler · Adım adım kod izleme

is-a ve has-a: Kod İzleme

YığınHeapAraba()thisbg90main()Araba #1hiz (Arac)0motornullArac kısmı (hiz) + Araba kısmı (motor)

new Araba(90): tek nesne, içinde üst sınıf Arac'tan gelen hiz ve kendi motor alanı var (is-a).

YığınHeapAraba()thisbg90main()Araba #1hiz (Arac)0motorMotor #2bg90

Yapıcı ikinci bir nesne oluşturur: araba bir motor değildir, motora sahiptir (has-a). Bağlantı bir referanstır.

YığınHeapyolaCik()thismain()Araba #1hiz (Arac)0motorMotor #2bg90

yolaCik() Araba nesnesi üzerinde çalışır.

YığınHeapcalistir()thisyolaCik()thismain()Araba #1hiz (Arac)0motorMotor #2bg90motor.calistir() → işi Motor nesnesi yapar

motor.calistir(): Araba işi kendi yapmaz, referansı izleyip Motor nesnesine devreder.

YığınHeapyolaCik()thismain()Araba #1hiz (Arac)50motorMotor #2bg90hizlan(50): Arac'tan kalıtımla gelen metot

hizlan(50) Araba'da yazılmadı; Arac'tan kalıtımla gelir ve aynı nesnenin hiz alanını 50 yapar.

YığınHeapyolaCik()thismain()Araba #1hiz (Arac)50motorMotor #2bg90

Kalıtım (hiz, hizlan) ve oluşum (motor) aynı sınıfta birlikte çalıştı.

class Motor {
    private int bg;
    Motor(int bg) { this.bg = bg; }
    void calistir() {
        System.out.println("Motor: " + bg + " BG");
    }
}
class Araba extends Arac {        // is-a
    private Motor motor;           // has-a
    Araba(int bg) { motor = new Motor(bg); }
    void yolaCik() {
        motor.calistir();  hizlan(50);
        System.out.println("Hız: " + hiz);
    }
}
new Araba(90).yolaCik();   // main

Sınıflar Arası İlişkiler

Çoklu Kalıtım

  • Java'da doğrudan çoklu kalıtım desteklenmez: bir sınıf yalnızca bir sınıftan türeyebilir.
  • Elmas problemi (diamond problem): B ve C yaz()'ı farklı geçersiz kılarsa D hangisini almalı?
  • Birden fazla sınıfın özellikleri gerekiyorsa: oluşum (composition) ve arayüzler (interface; bir sınıf birçok arayüz uygulayabilir).
A+ yaz()B+ yaz()C+ yaz()Dyaz() → ?
class D extends B, C { }   // geçersiz
error: '{' expected
interface Sarj { void sarjEt(); }
class ElektrikliAraba extends Araba
                      implements Sarj {
    @Override
    public void sarjEt() {
        System.out.println("Şarj ediliyor.");
    }
}

Sınıflar Arası İlişkiler

Kalıtımla Tasarım İlkeleri

“is-a” testi

“Her X bir Y midir?” cümlesi doğru değilse kalıtım kullanılmaz. Araba bir Motor değildir; Motor'a sahiptir → oluşum.

Herkes kendi verisini yönetir

Üst sınıfın alanlarına super(...) ve üst sınıfın metotlarıyla değer verilir; alanlar private tutulur.

Genel metotlar geçersiz kılınır

toString ve equals (ve onunla birlikte hashCode) her anlamlı sınıfta geçersiz kılınmalıdır.

Ortak kavramlar soyut sınıfa

Alt sınıfların ortak kavramları soyut (abstract) sınıflarla temsil edilmelidir: “Hayvan” tek başına nesne olmamalıysa.

Sınıflar Arası İlişkiler

Kalıtım mı, Oluşum mu?

Kalıtım (extends)Oluşum (alan olarak nesne)
İlişkiis-a: “her X bir Y'dir”has-a: “X'in bir Y'si vardır”
BağlılıkSıkı: alt sınıf, üst sınıfın gerçeklemesine de bağlıdırGevşek: yalnızca içteki nesnenin public metotlarına bağlıdır
Yeniden kullanımOtomatik: bütün public metotlar gelir (istenmeyenler de)Seçerek: yalnızca iletilmek istenen metotlar yazılır
DeğişiklikÜst sınıf derleme zamanında sabittirAlan, çalışma zamanında başka bir nesneyle değiştirilebilir
Üst tip yerine kullanımDoğrudan: Hayvan h = new Kedi()Ortak bir arayüz uygulanırsa
Kalıtım uygundur

is-a ilişkisi gerçekten doğruysa ve üst sınıf genişletilmek üzere tasarlanıp belgelendiyse: Kedi – Hayvan.

Oluşum uygundur

Amaç yalnızca başka bir sınıfın kodunu kullanmaksa: Yigin, bir ArrayList değildir; listeyi içinde tutar ve yalnızca ekle/cikar sunar.

Sınıflar Arası İlişkiler · Adım adım kod izleme

Kırılgan Üst Sınıf: Kod İzleme

Yığın (stack)Heapmain()lSayanListe #1eklemeSayisi0boyut0ogeler[ ]boyut, ogeler: Liste kısmı (private)üç öğe eklenecek: beklenen sayaç 3

SayanListe, eklenen öğe sayısını tutmak için Liste'den türetildi. İki ekleme metodu da geçersiz kılındı.

Yığın (stack)HeapSayanListe.hepsiniEklemain()lSayanListe #1eklemeSayisi3boyut0ogeler[ ]boyut, ogeler: Liste kısmı (private)dizi.length = 3 eklendi

Alt sınıfın hepsiniEkle'si sayaca dizinin uzunluğunu ekler: 3. Sonra işi üst sınıfa bırakır.

Yığın (stack)HeapListe.hepsiniEkleSayanListe.hepsiniEklemain()lSayanListe #1eklemeSayisi3boyut0ogeler[ ]boyut, ogeler: Liste kısmı (private)Liste döngüsü kendi ekle()'sini çağırır

Liste.hepsiniEkle her öğe için ekle(s), yani this.ekle(s) çağırır. Bu, üst sınıfın bir gerçekleme ayrıntısıdır.

Yığın (stack)HeapSayanListe.ekleListe.hepsiniEkleSayanListe.hepsiniEklemain()lSayanListe #1eklemeSayisi4boyut0ogeler[ ]boyut, ogeler: Liste kısmı (private)this bir SayanListe → geçersiz kılınmış ekle

Nesne bir SayanListe olduğu için çağrı alt sınıfın ekle'sine gider. Aynı öğe ikinci kez sayılır: 4.

Yığın (stack)HeapListe.ekleSayanListe.ekleListe.hepsiniEkleSayanListe.hepsiniEklemain()lSayanListe #1eklemeSayisi4boyut1ogeler[a]boyut, ogeler: Liste kısmı (private)super.ekle: öğe gerçekten eklenir

super.ekle(s) öğeyi diziye yazar: boyut = 1.

Yığın (stack)HeapListe.hepsiniEkleSayanListe.hepsiniEklemain()lSayanListe #1eklemeSayisi6boyut3ogeler[a, b, c]boyut, ogeler: Liste kısmı (private)"b" ve "c" için aynı yol

"b" ve "c" için de aynı yol izlenir: her biri bir kez daha sayılır. Döngü bitince sayaç 6, boyut 3.

Yığın (stack)Heapmain()lSayanListe #1eklemeSayisi6boyut3ogeler[a, b, c]boyut, ogeler: Liste kısmı (private)üç öğe, sayaç 6: kırılgan üst sınıf

Yanlış sonuç: 6 3. Alt sınıf, üst sınıfın kendi metotlarını nasıl kullandığına bağlı kaldı. Üst sınıfın yeni bir sürümü döngüyü değiştirse sonuç yine değişirdi: kırılgan üst sınıf (fragile base class).

class Liste {
    private String[] ogeler = new String[10];
    private int boyut;
    void ekle(String s) { ogeler[boyut++] = s; }
    void hepsiniEkle(String[] dizi) {
        for (String s : dizi) ekle(s);
    }
    int boyut() { return boyut; }
}
class SayanListe extends Liste {
    int eklemeSayisi = 0;
    @Override void ekle(String s) {
        eklemeSayisi++;
        super.ekle(s);
    }
    @Override void hepsiniEkle(String[] dizi) {
        eklemeSayisi += dizi.length;
        super.hepsiniEkle(dizi);
    }
}
SayanListe l = new SayanListe();   // main
l.hepsiniEkle(new String[] {"a", "b", "c"});
System.out.println(l.eklemeSayisi + " " + l.boyut());

Sınıflar Arası İlişkiler

Oluşumla Çözüm: İletme

class SayanListe2 {
    private final Liste liste = new Liste(); // has-a
    private int eklemeSayisi = 0;
    void ekle(String s) {
        eklemeSayisi++;
        liste.ekle(s);            // iletir
    }
    void hepsiniEkle(String[] dizi) {
        eklemeSayisi += dizi.length;
        liste.hepsiniEkle(dizi);  // iletir
    }
    int getEklemeSayisi() { return eklemeSayisi; }
    int boyut() { return liste.boyut(); }
}
SayanListe2 l2 = new SayanListe2();
l2.hepsiniEkle(new String[] {"a", "b", "c"});
System.out.println(l2.getEklemeSayisi()
    + " " + l2.boyut());
3 3
  • Yeni sınıf bir Liste'den türemez, bir Liste'ye sahiptir. Her metot önce kendi işini yapar, sonra çağrıyı içteki nesneye iletir (forwarding).
  • liste.hepsiniEkle(...) içindeki ekle çağrıları düz bir Liste nesnesinde çalışır; SayanListe2'ye geri dönmez. Sayaç doğrudur: 3.
  • Liste'nin iç ayrıntısı değişse de sonuç değişmez: bağlılık yalnızca public metotlaradır.
  • Bedeli: SayanListe2 bir Liste beklenen yere verilemez. Gerekirse ikisi ortak bir arayüzü uygular.

Sınıflar Arası İlişkiler

Yerine Koyma İlkesi (LSP)

class Dikdortgen {
    protected int en, boy;
    void setEn(int en) { this.en = en; }
    void setBoy(int boy) { this.boy = boy; }
    int alan() { return en * boy; }
}
class Kare extends Dikdortgen {
    @Override void setEn(int k) { en = k; boy = k; }
    @Override void setBoy(int k) { en = k; boy = k; }
}
static void genislet(Dikdortgen d) {
    d.setEn(5);
    d.setBoy(4);
    System.out.println("Beklenen 20, bulunan "
        + d.alan());
}
// main içinde:
genislet(new Dikdortgen());
genislet(new Kare());
Beklenen 20, bulunan 20
Beklenen 20, bulunan 16
  • Liskov yerine koyma ilkesi (Liskov substitution principle): üst sınıf tipiyle yazılmış kod, bir alt sınıfın nesnesiyle de doğru çalışmalıdır. Derlenmesi yetmez.
  • Matematikte her kare bir dikdörtgendir. Ama bu Dikdortgen sınıfı “en ve boy birbirinden bağımsız değişir” sözünü verir; Kare bu sözü tutamaz.
  • genislet hatalı değildir; hata, sözü bozan alt sınıftadır.
  • Çözümler: Kare'yi Dikdortgen'den türetmemek (ikisi de Sekil'den türer) ya da sınıfları değiştirilemez yapmak.
is-a testi gerçek dünyaya değil, sınıfın davranışına uygulanır: “Kare, Dikdortgen'in her metoduna onun gibi cevap verebilir mi?”

Object Sınıfı

Object Sınıfı

  • Her sınıf java.lang.Object sınıfından türer; extends yazılmamışsa dolaylı olarak.
  • Bu yüzden her nesnede aşağıdaki metotlar hazır bulunur.
  • Varsayılan sürümleri genellikle işimize yaramaz; anlamlı sınıflarda geçersiz kılınırlar.
class Nokta { }
// derleyicinin gördüğü:
class Nokta extends Object { }
MetotVarsayılan davranış
String toString()Sınıf adı + @ + hashCode değerinin onaltılık (hex) yazımı
boolean equals(Object o)Referansları karşılaştırır: this == o
int hashCode()Nesneye özgü bir tam sayı (genellikle her nesnede farklı)
Class getClass()Nesnenin çalışma zamanındaki sınıfı (final; geçersiz kılınamaz)

Object Sınıfı

toString Metodu

  • public String toString(): nesneyi metin olarak gösterir. println(nesne) ve "..." + nesne onu kendiliğinden çağırır.
  • Geçersiz kılınmazsa sınıf adı ve nesnenin hashCode değerini içeren bir dizgi döner.

Geçersiz kılınmadan

class Nokta {
    private int x, y;
    Nokta(int x, int y) {
        this.x = x; this.y = y;
    }
}
// main içinde
System.out.println(new Nokta(3, 4));
Nokta@28a418fc

@ sonrası değer her çalıştırmada değişebilir; okunaklı değildir. null bir referans yazdırılırsa toString çağrılmaz, null yazılır.

Geçersiz kılınarak

class Nokta {
    private int x, y;
    Nokta(int x, int y) {
        this.x = x; this.y = y;
    }
    @Override
    public String toString() {
        return "Nokta[x=" + x + ", y=" + y + "]";
    }
}
// main içinde
Nokta n = new Nokta(3, 4);
System.out.println(n);
System.out.println("Konum: " + n);
Nokta[x=3, y=4]
Konum: Nokta[x=3, y=4]

Object Sınıfı · Adım adım kod izleme

== mi, equals mı?

k1k2—k3—Kisi nesnesi #1ad = "Ali"yas = 25

new bir nesne oluşturur; k1 onun adresini (referansını) tutar.

k1k2k3—Kisi nesnesi #1ad = "Ali"yas = 25Kisi nesnesi #2ad = "Ali"yas = 25

İkinci new: içeriği aynı, ama ayrı bir nesne. k2 onu gösterir.

k1k2k3Kisi nesnesi #1ad = "Ali"yas = 25Kisi nesnesi #2ad = "Ali"yas = 25

k3 = k1 nesne kopyalamaz; yalnızca adresi kopyalar. k1 ve k3 aynı nesneyi gösterir.

k1k2k3Kisi nesnesi #1ad = "Ali"yas = 25Kisi nesnesi #2ad = "Ali"yas = 25#1 ≠ #2 → false

== referansları karşılaştırır: k1 ve k2 farklı nesneleri gösterir → false.

k1k2k3Kisi nesnesi #1ad = "Ali"yas = 25Kisi nesnesi #2ad = "Ali"yas = 25#1 = #1 → true

k1 ve k3 aynı nesneyi gösterir → true.

k1k2k3Kisi nesnesi #1ad = "Ali"yas = 25Kisi nesnesi #2ad = "Ali"yas = 25içerik aynı → true

equals, Kisi'de geçersiz kılındığı için içeriği (ad, yas) karşılaştırır → true. Geçersiz kılınmasaydı false olurdu.

Kisi k1 = new Kisi("Ali", 25);
Kisi k2 = new Kisi("Ali", 25);
Kisi k3 = k1;
System.out.println(k1 == k2);
System.out.println(k1 == k3);
System.out.println(k1.equals(k2));

Object Sınıfı

equals Metodu

  • public boolean equals(Object other): iki nesnenin eşdeğer olup olmadığını söyler.
  • Varsayılan olarak yalnızca referansları karşılaştırır: geçersiz kılınmazsa == işleci gibi davranır.
  • Geçersiz kılınarak davranışı değiştirilir: metot iki nesnenin durumunu (alanlarını) karşılaştırmalı ve eşitse true dönmelidir.
  • Parametre Object tipindedir; hatalı durumları engellemek için önce tip kontrolü yapılmalıdır.

equals sözleşmesi

Yansımax.equals(x) → true
Simetrix.equals(y) ⇔ y.equals(x)
Geçişmex = y ve y = z ise x = z
TutarlılıkAlanlar değişmedikçe sonuç değişmez
nullx.equals(null) → false
String karşılaştırmasında da kural aynıdır: s1 == s2 değil, s1.equals(s2).
abNokta(3, 4) #1Nokta(3, 4) #2a == bfalseObject.equals (varsayılan)falsegeçersiz kılınmış equalstrue

Object Sınıfı

equals Metodunu Yazmak

instanceof ile tip kontrolü

public boolean equals(Object nesne) {
    if (nesne instanceof Nokta) {
        // nesne bir Nokta nesnesidir;
        // dönüştürüp karşılaştır
        Nokta diger = (Nokta) nesne;
        return x == diger.x && y == diger.y;
    } else {
        // nesne bir Nokta değildir;
        // eşit olamaz
        return false;
    }
}
  • Parametre tipi Object olmalıdır; equals(Nokta n) yazmak geçersiz kılma değil, aşırı yüklemedir.
  • Dönüşüm ((Nokta) nesne) yalnızca tip kontrolünden sonra güvenlidir.

Deneme

Nokta a = new Nokta(3, 4);
Nokta b = new Nokta(3, 4);
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a.equals("3,4"));
System.out.println(a.equals(null));
false
true
false
false
"3,4" bir Nokta değildir → false. null instanceof Nokta da false olduğu için ayrı bir null kontrolüne gerek kalmaz.

Object Sınıfı

getClass() ile equals

public class Kisi {
    private String ad;
    private int yas;
    // ... yapıcı ...

    @Override
    public boolean equals(Object obj) {
        // aynı referanssa eşittir
        if (this == obj) return true;
        // null ya da farklı sınıf: eşit değil
        if (obj == null
                || getClass() != obj.getClass())
            return false;
        // alanları karşılaştır
        Kisi kisi = (Kisi) obj;
        return yas == kisi.yas
               && ad.equals(kisi.ad);
    }
}
  • this == obj: aynı nesneyse hemen true (hızlı yol).
  • getClass() nesnenin gerçek sınıfını verir; iki tarafın sınıfı birebir aynı olmalıdır.
  • İlkel tipler (int yas) == ile, nesne tipindeki alanlar (String ad) equals ile karşılaştırılır.
Tip kontrolüAlt sınıf nesnesi eşit sayılabilir mi?
instanceofEvet (Kedi nesnesi bir Hayvan'dır)
getClass()Hayır (sınıflar birebir aynı olmalı)

Object Sınıfı · Adım adım kod izleme

equals(): Kod İzleme

YığınHeapmain()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21

Üç ayrı nesne var. k1 ile k2 içerikçe aynı, k3 yalnızca yaşta farklı.

YığınHeapequals()thisobjkisi?main()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21

k1.equals(k2): yeni çerçevede this = #1, obj = #2. obj'nin tipi Object.

YığınHeapequals()thisobjkisi?main()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21this == obj → false

this == obj adresleri karşılaştırır: #1 ≠ #2, hızlı yol işlemez.

YığınHeapequals()thisobjkisi?main()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21null? false · Kisi != Kisi? false

obj null değil; getClass() iki tarafta da Kisi. Koşul yanlış, return false atlanır.

YığınHeapequals()thisobjkisimain()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21

Aşağı dönüşüm yeni nesne üretmez: kisi aynı #2'yi, bu kez Kisi tipiyle gösterir; artık kisi.yas yazılabilir.

YığınHeapequals()thisobjkisimain()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas2120 == 20 true && "Ali".equals("Ali") true

İlkel yas == ile, String olan ad equals ile karşılaştırılır. İkisi de doğru: true.

YığınHeapequals()thisobjkisi?main()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21this == obj false · aynı sınıf

k1.equals(k3): yeni çağrı, obj = #3. Farklı nesne, aynı sınıf: alanlara geçilir.

YığınHeapequals()thisobjkisimain()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas2120 == 21 false → ad.equals çağrılmaz

20 == 21 yanlış; && kısa devre yapar, ad.equals hiç çağrılmaz. Sonuç false.

YığınHeapequals()thisobjnullkisi?main()k1k2k3Kisi #1ad"Ali"yas20Kisi #2ad"Ali"yas20Kisi #3ad"Ali"yas21obj == null true → getClass() atlanır

obj == null doğru; || sağ tarafı değerlendirmez. obj.getClass() çalışsaydı NullPointerException olurdu.

public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass())
        return false;
    Kisi kisi = (Kisi) obj;
    return yas == kisi.yas && ad.equals(kisi.ad);
}
// main içinde:
Kisi k1 = new Kisi("Ali", 20);
Kisi k2 = new Kisi("Ali", 20);
Kisi k3 = new Kisi("Ali", 21);
System.out.println(k1.equals(k2));
System.out.println(k1.equals(k3));
System.out.println(k1.equals(null));

Object Sınıfı

equals ve hashCode

Kural: equals geçersiz kılınıyorsa hashCode da geçersiz kılınmalıdır. Eşit nesnelerin hashCode değerleri aynı olmalıdır.
import java.util.Objects;
public class Nokta {
    private int x, y;
    public Nokta(int x, int y) {
        this.x = x; this.y = y;
    }
    @Override public boolean equals(Object o) {
        return (o instanceof Nokta n)
               && x == n.x && y == n.y;
    }
    @Override public int hashCode() {
        return Objects.hash(x, y);
    }
    @Override public String toString() {
        return "Nokta[x=" + x + ", y=" + y + "]";
    }
}

Neden? HashSet ve HashMap önce hashCode ile “kova” seçer, sonra equals çağırır. hashCode farklıysa eşit nesneler hiç karşılaştırılmaz.

HashSet<Nokta> kume = new HashSet<>();
kume.add(new Nokta(3, 4));
kume.add(new Nokta(3, 4));
System.out.println(kume.size() + " "
    + kume.contains(new Nokta(3, 4)));
Nokta sınıfındaÇıktı
yalnızca equals2 false ✗
equals + hashCode1 true ✓

o instanceof Nokta n örüntü eşlemesi Java 16+ özelliğidir; klasik biçim: if (o instanceof Nokta) { Nokta n = (Nokta) o; … }.

Object Sınıfı · Adım adım kod izleme

HashSet İçinde equals ve hashCode: Kod İzleme

kume: 16 kova (kova = hash % 16)0123456789101112131415

Boş bir HashSet: öğeler 16 kovadan birine konur. Kovayı hashCode() seçer.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)Nokta(3,4)31·34 + 4 = 10581058 % 16 = 2 → kova boş, eklenir

hashCode() = 31·(31+3)+4 = 1058; 1058 % 16 = 2. Kova boş: nesne eklenir.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)(4,3)Nokta(4,3)31·35 + 3 = 10881088 % 16 = 0 → kova boş, eklenir

Aynı sayılar, farklı sıra: hash 1088, kova 0. Yine boş kova, eklenir.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)(4,3)Nokta(3,4) yeni1058 → kova 2

Yeni bir Nokta(3, 4) nesnesi: hash yine 1058, aynı kova 2. Kova dolu: içindekiyle equals sorulur.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)(4,3)Nokta(3,4) yeniequals → true3 == 3 && 4 == 4 → true: eklenmez

equals doğru: küme aynı öğeyi ikinci kez almaz, add false döndürür.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)(4,3)

Kümede iki öğe var: 2.

kume: 16 kova (kova = hash % 16)0123456789101112131415(3,4)(4,3)Nokta(4,3) yeni1088 → kova 0kova 0 → equals true → bulunduhashCode yoksa: farklı kova, equals sorulmaz

contains da önce kovayı bulur, sonra equals sorar: true. hashCode yazılmasaydı eşit nesneler farklı kovalara düşerdi.

public boolean equals(Object o) {
    if (!(o instanceof Nokta)) return false;
    Nokta n = (Nokta) o;
    return x == n.x && y == n.y;
}
public int hashCode() {
    return Objects.hash(x, y);  // 31·(31 + x) + y
}
// main içinde:
HashSet<Nokta> kume = new HashSet<>();
kume.add(new Nokta(3, 4));
kume.add(new Nokta(4, 3));
kume.add(new Nokta(3, 4));
System.out.println(kume.size());
System.out.println(kume.contains(new Nokta(4, 3)));

Özet

Özet

KavramÖzü
Kalıtımclass Alt extends Ust; alt sınıf üst sınıfın üyelerini kalıtımla alır, yenilerini ekler (is-a)
Kalıtımla alınmayanYapıcılar; private üyeler nesnede vardır ama doğrudan erişilemez
supersuper(...): üst sınıf yapıcısı (ilk satır); super.m(): üst sınıfın sürümü
Yapıcı zinciriObject → … → Ust → Alt sırasıyla tamamlanır
Geçersiz kılmaAynı imza, alt sınıfta yeni gövde; @Override ile derleyici denetler
protectedSınıf + alt sınıflar + aynı paket
finalMetot: geçersiz kılınamaz; sınıf: alt sınıfı olamaz
Has-aOluşum: başka sınıf tipinde alan; çoklu kalıtım yerine oluşum ve arayüzler
ObjectHer sınıfın atası; toString, equals, hashCode geçersiz kılınır
== / equalsReferans karşılaştırma / içerik karşılaştırma

Özet

Sık Yapılan Hatalar

super(...) çağırmamak

Üst sınıfta parametresiz yapıcı yoksa: constructor Hayvan in class Hayvan cannot be applied to given types.

private alana doğrudan erişmek

saglik += 5; alt sınıfta: saglik has private access in Hayvan. Çözüm: getter ya da protected metot.

equals(Nokta) yazmak

Parametre Object değilse aşırı yüklemedir; @Override ile: method does not override or implement a method from a supertype.

Nesneleri == ile karşılaştırmak

İçerikleri aynı iki nesne için == → false. String dahil, içerik equals ile karşılaştırılır.

equals var, hashCode yok

Derleme hatası yoktur ama HashSet/HashMap yanlış çalışır: aynı nokta iki kez eklenir.

İki sınıftan türetmeye çalışmak

class D extends B, C → '{' expected. Çözüm: oluşum ya da arayüz.

Erişimi daraltmak

public void ciz() alt sınıfta void ciz() olursa: attempting to assign weaker access privileges.

Kontrolsüz aşağı dönüşüm

(Lastik) p, p bir Lastik değilse: ClassCastException. Önce instanceof.

Özet

Sık Yapılan Hatalar (2)

this(...) ile super(...) birlikte

İkisi de ilk satır olmak ister: call to super must be first statement in constructor. Zincirin sonundaki yapıcı super(...) çağırır.

Dönüş tipini değiştirmek

String yavrula(): return type String is not compatible with Hayvan. Yalnızca alt tip (kovaryant) olabilir.

private metodu geçersiz kıldığını sanmak

Hata vermez ama üst sınıfın kodu yine kendi private sürümünü çağırır. @Override bu yanılgıyı yakalar.

protected üyeye üst tip referansıyla erişmek

Başka paketteki alt sınıfta h.isim: isim has protected access in Hayvan. Erişim alt sınıfın kendi tipi üzerinden yapılır.

İstisna listesini genişletmek

void f() throws Exception, üstte throws yoksa: overridden method does not throw Exception.

Alan sırasını yanlış sanmak

Alt sınıfın alan ilk değerleri, üst sınıf yapıcısı bittikten sonra atanır; üst yapıcı çalışırken hâlâ varsayılandır.

Kod paylaşmak için kalıtım

is-a doğru değilse alt sınıf, istenmeyen public metotları da alır: Yigin extends ArrayList ortadan eklemeye izin verir. Çözüm: oluşum.

Kendini çağıran metodu geçersiz kılmak

Üst sınıfın öteki metotları da alt sınıfın sürümünü çağırabilir: sayaç iki kez artar. Çözüm: üst sınıfın belgesine bakmak ya da oluşum.

Özet

Örnek Problemler

class A {
    A() { System.out.println("A"); }
    void yaz() { System.out.println("A.yaz"); }
}
class B extends A {
    B() { System.out.println("B"); }
    @Override void yaz() { System.out.println("B.yaz"); }
}
class C extends B {
    C() { super(); System.out.println("C"); }
    @Override void yaz() {
        super.yaz();
        System.out.println("C.yaz");
    }
}
  1. C c = new C(); ne yazdırır?
  2. Ardından c.yaz(); ne yazdırır?
  3. A a = new B(); a.yaz(); ne yazdırır?
  4. c instanceof A nedir?
  5. equals'ı geçersiz kılmayan bir Ogrenci sınıfında, aynı adlı iki nesne için o1.equals(o2) nedir?
Cevapları göster
  1. A B C: yapıcılar üstten alta tamamlanır (B'nin gizli super()'ı önce A'yı çalıştırır).
  2. B.yaz, sonra C.yaz: super.yaz() B'nin sürümünü çağırır.
  3. A B, sonra B.yaz: değişken A tipinde ama nesne B olduğu için B'nin sürümü çalışır.
  4. true: C, B üzerinden A'dan türer.
  5. false: Object'in equals'ı referansları karşılaştırır.

Özet

Örnek Problemler (2)

class A {
    static { System.out.print("As "); }
    { System.out.print("Ab "); }
    A() { System.out.print("A "); }
}
class B extends A {
    static { System.out.print("Bs "); }
    { System.out.print("Bb "); }
    B() { this(0); System.out.print("B "); }
    B(int x) { System.out.print("B" + x + " "); }
}
  1. Programdaki ilk new B(); ne yazdırır? (Süslü parantezli satırlar örnek ilklendirici bloklarıdır.)
  2. Ardından new B(7); ne yazdırır?
  3. Yerine koyma örneğindeki Kare ile Dikdortgen d = new Kare(); d.setBoy(4); d.setEn(5); sonrasında d.alan() kaçtır?
  4. Ust'te protected Object kopya() var. Alt extends Ust içinde @Override public Alt kopya() derlenir mi?
Cevapları göster
  1. As Bs Ab A Bb B0 B: static bloklar ilk kullanımda üstten alta çalışır. B()'nin ilk satırı this(0) olduğu için B'nin örnek bloğu (Bb) yalnızca bir kez, B(int) içinde super() döndükten sonra çalışır.
  2. Ab A Bb B7: static bloklar tekrar çalışmaz.
  3. 25: son çağrı setEn(5); Kare iki kenarı da 5 yapar.
  4. Evet: erişim genişler (protected → public) ve Alt, Object'in alt tipidir (kovaryant dönüş).

Özet

Alıştırmalar

  1. Calisan (ad, maas) ve ondan türeyen Yonetici (prim) yazın; toplamGelir() ve super.toString() kullanan bir toString() ekleyin.
  2. Hayvan'ın tek yapıcısı Hayvan(String isim). Kus(String isim, boolean ucabilir) yapıcısını yazın.
  3. Ogrenci (int no, String ad) için equals ve hashCode yazın.
  4. Başka paketteki, alt sınıf olmayan bir sınıf protected bir alana erişebilir mi?
  5. is-a mı, has-a mı? Öğrenci–Kişi, Araba–Tekerlek, Kütüphane–Kitap, Kare–Şekil.
Cevaplar / ipuçları
  1. Yapıcı: super(ad, maas); this.prim = prim; · toplamGelir(): getMaas() + prim. new Yonetici("Ayşe", 50000, 8000) → 58000.0.
  2. Kus(String isim, boolean ucabilir) { super(isim); this.ucabilir = ucabilir; }
  3. if (!(o instanceof Ogrenci)) return false; Ogrenci d = (Ogrenci) o; return no == d.no && ad.equals(d.ad); · return Objects.hash(no, ad);
  4. Hayır: protected = sınıf + aynı paket + alt sınıflar.
  5. is-a: Öğrenci–Kişi, Kare–Şekil · has-a: Araba–Tekerlek, Kütüphane–Kitap.

Özet

Kaynaklar

  • Oracle, The Java Tutorials: Inheritance; Overriding and Hiding Methods; Using the Keyword super; Object as a Superclass.
  • J. Gosling ve diğ., The Java Language Specification, Java SE 21 Edition (8.1.4 Superclasses and Subclasses, 8.4.8 Inheritance, Overriding, and Hiding).
  • P. Deitel ve H. Deitel, Java: How to Program, 11. baskı, Pearson (Bölüm 9: Object-Oriented Programming: Inheritance).
  • K. Sierra ve B. Bates, Head First Java, 3. baskı, O'Reilly (Bölüm 7: Inheritance and Polymorphism).
  • C. S. Horstmann, Core Java, Volume I: Fundamentals, 12. baskı, Pearson (Bölüm 5: Inheritance).
  • J. Bloch, Effective Java, 3. baskı, Addison-Wesley (Madde 10–11: equals ve hashCode; Madde 18: oluşumu kalıtıma tercih edin).

Son

Bölüm 4.3: Kalıtım
Sonraki bölüm: Bölüm 4.4: Çok Biçimlilik · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.