Bölüm 4: 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-aÇoklu kalıtımObject sınıfıtoString, equals, hashCodeinstanceof
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özdizimi

extends ile alt sınıf yazmak; neyin miras 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 nitelik ve metotlarını miras 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, temel sınıf).
  • Yeni sınıf: alt sınıf (subclass, türetilmiş sınıf).
  • Alt sınıfa yeni nitelik ve metotlar eklenebilir; miras alınan nitelikler 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ı niteliği 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.

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 miras alır.
ObjectHayvanStringSekilKediKopekDikdortgenyukarı: genel · aşağı: özel

Kalıtım Kavramı

Neler Miras Alınır?

ÜyeAlt sınıfa geçer mi?Not
public / protected nitelik 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 niteliklerNesnede 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ırMiras 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 nitelikleri vardır; hangisine doğrudan erişilebileceğini erişim belirteci belirler.

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 nitelik 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 nitelikler 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 · Kodu adım adım çalıştır

Hangi Metot Çalışır?

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)

new Kedi("Mavi"): nesne oluşur. İçinde Hayvan'dan gelen isim ve saglik alanları da vardır.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Kedi'de bulundu

kedi.konus(): arama nesnenin sınıfı Kedi'de başlar. konus() orada var: geçersiz kılınmış sürüm seçilir.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Kedi'de bulundu

getIsim() Hayvan'dan miras; private isim'e onun aracılığıyla ulaşılır.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Kedi'de bulundu

kedi.avlan(): Kedi'ye özgü metot; Kedi'de bulunur.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Kedi'de bulundu

avlan() gövdesi çalışır ve ikinci satır yazılır.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✗ Kedi'de yok → üste✓ Hayvan'da bulundu

kedi.yemekYe(5.0): Kedi'de yok → bir üst sınıfa, Hayvan'a bakılır ve orada bulunur.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Hayvan'da bulundu

Miras alınan metot, aynı nesnenin saglik alanını günceller: 100.0 + 5.0.

Kedikonus()avlan()HayvangetIsim()konus()yemekYe(double)✓ Hayvan'da bulundu

Sonuç yazılır. Kural: metot önce nesnenin kendi sınıfında, yoksa yukarı doğru üst sınıflarda aranır.

class Hayvan {
    private String isim;
    private double saglik = 100.0;
    Hayvan(String isim) { this.isim = isim; }
    String getIsim() { return isim; }
    void konus() { System.out.println("Ses yok."); }
    void yemekYe(double m) {
        saglik += m;
        System.out.println(isim + ": " + saglik);
    }
}
class Kedi extends Hayvan {
    Kedi(String isim) { super(isim); }
    @Override void konus() {
        System.out.println(getIsim() + " miyavlar.");
    }
    void avlan() {
        System.out.println(getIsim() + " avlandı!");
    }
}
Kedi kedi = new Kedi("Mavi");   // main
kedi.konus();
kedi.avlan();
kedi.yemekYe(5.0);

Hayvan Örneği

Kalıtımın Avantajları

Yeniden kullanım

Üst sınıfın metot ve nitelikleri 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 Kelimesi

  • 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 niteliklerine ilk değerleri o atar.
  • super(...) yapıcının ilk satırı olmalıdır.
  • 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 · Kodu adım adım çalıştır

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ını açıkça çağırın: 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.

Geçersiz Kılma

Metodu Geçersiz Kılma

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

  • Ad ve parametre listesi aynı olmalı.
  • Dönüş türü aynı (veya onun alt türü) 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, miras alınan metot içinAynı sınıfta (veya alt sınıfta)
AdAynıAynı
ParametrelerAynıFarklı (sayı ya da tür)
Dönüş türüAynı veya alt türüSerbest
AmaçMiras alınan davranışı değiştirmekAynı işi farklı girdilerle yapmak
Seçim zamanıÇalışma zamanı (nesnenin gerçek türü)Derleme zamanı (argüman türleri)
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 bir sonraki bölümün konusu olan çok biçimliliğin (polymorphism) temelidir.

Geçersiz Kılma

@Override Etiketi

  • @Override bir etikettir (annotation): metodun, üst sınıftan miras 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ı.

Erişim ve Sınırlama

protected Erişim Seviyesi

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 niteliklerine alt sınıf, yalnızca üst sınıftan miras alınan public veya protected metotlar aracılığıyla erişebilir.

İyi uygulama: nitelikler 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

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

  • final anahtar kelimesi 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).

Erişim ve Sınırlama

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

  • Bir nesne, üst sınıf türünde 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 türü 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 tür kontrolü, sonra aşağı dönüşüm (downcasting) gerekir: instanceof slaytına bakın.

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 türünde nitelik 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

Ç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, ayrıntısı “Soyut Sınıflar” bölümünde).
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 kullanmayın. 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 verin; alanları private tutun.

Genel metotları geçersiz kılın

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.

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 tamsayı (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 dize 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.

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ı · Kodu adım adım çalıştır

== 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 == operatörü 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 türündedir; hatalı durumları engellemek için önce tür 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).

Object Sınıfı

instanceof Operatörü

  • Bir nesnenin belirli bir sınıfa veya arayüze uyup uymadığını kontrol eder.
  • Kullanım: nesne instanceof Tür; sonuç true veya false.
  • Alt sınıf nesnesi, üst sınıfın da örneğidir.
  • null bir nesne kabul edilmez: null instanceof Tür her zaman false döner. Bu yüzden instanceof ayrıca null kontrolü gerektirmez.
  • Aşağı dönüşümden (downcasting) önce kullanılır; yanlış dönüşüm çalışma zamanında ClassCastException fırlatır.
Exception in thread "main"
java.lang.ClassCastException: class Parca
cannot be cast to class Lastik
Parca p = new Lastik("Petlas", "XYZ-123",
                     "225/45R17", true);
if (p instanceof Lastik) {
    Lastik l = (Lastik) p;   // güvenli dönüşüm
    System.out.println(l.kislikMi());
}
System.out.println(p instanceof Parca);
System.out.println(p instanceof Object);
Parca bos = null;
System.out.println(bos instanceof Parca);
Parca duz = new Parca("Bosch", "ABC-9");
System.out.println(duz instanceof Lastik);
true
true
true
false
false

Java 16+: örüntü eşleme

if (p instanceof Lastik l) {   // kontrol + dönüşüm
    System.out.println(l.kislikMi());
}

Object Sınıfı

equals Metodunu Yazmak

instanceof ile tür 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 türü 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 tür 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.
  • Temel türler (int yas) == ile, nesne türündeki alanlar (String ad) equals ile karşılaştırılır.
Tür 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ı

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 önceki slayttaki gibidir.

Özet

Özet

KavramÖzü
Kalıtımclass Alt extends Ust; alt sınıf üst sınıfın üyelerini miras alır, yenilerini ekler (is-a)
Miras 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 türünde nitelik; ç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. Getter veya protected metot kullanın.

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, equals kullanın.

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. Oluşum veya arayüz kullanın.

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

Ö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 türünde 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

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: Kalıtım
Sonraki bölüm: Bölüm 4: Çok Biçimlilik
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.