Bölüm 4.3: Kalıtım
Giriş
Öğrenme Çıktıları
Kalıtımı (inheritance), üst sınıf ve alt sınıf kavramlarını tanımlamak; “is-a” ilişkisini tanımak.
extends ile alt sınıf yazmak; neyin kalıtımla alındığını, neyin alınmadığını açıklamak.
super(...) ve super.metot() kullanmak; yapıcı zincirinin çalışma sırasını izlemek.
Metodu geçersiz kılmak (overriding), aşırı yüklemeden (overloading) ayırmak, @Override kullanmak.
protected erişimini ve final ile kalıtımın nasıl sınırlandığını açıklamak.
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.
en, boy ve alan() bilgisini ekler.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”. extendssonrasına yalnızca bir sınıf yazılabilir.- Alt sınıf, üst sınıfın
publicveprotectedü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.
extendsyazılmazsa sınıf, gizliceObjectsı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.langpaketindekiObjectsınıfı ile başlar. - Her sınıf, doğrudan veya dolaylı olarak
Objectsınıfından kalıtımla alır.
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 üyeleriniHayvanü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.
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.Kalıtım Kavramı
Neler Kalıtımla Alınır?
| Üye | Alt sınıfa geçer mi? | Not |
|---|---|---|
public / protected alan ve metotlar | Evet | Alt sınıfta doğrudan kullanılır. |
| Belirteçsiz (paket erişimli) üyeler | Yalnızca aynı paketteyse | Başka paketteki alt sınıf erişemez. |
private alanlar | Nesnede yer alır, ama doğrudan erişilemez | Üst sınıfın public/protected metotlarıyla (getter, setter) kullanılır. |
private metotlar | Hayır | Görünmez; geçersiz kılınamaz. |
| Yapıcılar (constructors) | Hayır | Kalıtımla alınmaz; alt sınıf yapıcısı super(...) ile çağırır. |
static üyeler | Erişilebilir | Sı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.
privateolsalar bile oradadırlar; yalnızcaKedi'nin kodu onlara adıyla erişemez. superayrı bir nesne değildir: aynı nesnenin üst sınıf kısmına, üst sınıfın kodu üzerinden ulaşmanın yoludur.
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
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ı
Üst sınıfın metot ve alanları alt sınıfta yeniden yazılmadan kullanılır.
Mevcut sınıf, kodu değiştirilmeden yeni özelliklerle genişletilir.
Üst sınıfın metotları alt sınıfta geçersiz kılınarak (overriding) değiştirilebilir.
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
isimalanına kendisi atama yapmaz; işisuper(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
Nesne bir Kedi; arama en alttan başlar ve Kedi.tanit() bulunur.
super.tanit(): bir üst sınıfın sürümü çağrılır. Kedi.tanit bekler, yığında kalır.
Nesne hâlâ bir Kedi olsa da Hayvan içindeki super sabit olarak Canli'yi gösterir: dinamik bağlama yok.
En üstteki sürüm çalışır ve ilk satırı yazar; sonra döner.
Hayvan.tanit() kaldığı yerden sürer: Hayvanım.
Son olarak Kedi.tanit() kendi satırını yazar.
Ç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)
new: önce bellekte nesneye yer ayrılır; tüm alanlar (Hayvan'ınkiler dahil) varsayılan değerleri alır.
Kedi yapıcısı çağrılır. Kendi işine başlamadan önce üst sınıf kısmı hazırlanmalıdır.
super(isim): Hayvan yapıcısı çağrılır; Kedi yapıcısı bekler.
Hayvan da önce kendi üst sınıfını çağırır: Object(). Her zincir Object'te biter.
Object() döndü. Hayvan kendi alanını doldurur: isim = "Mavi".
saglik = 100.0.
Hayvan yapıcısı mesajını yazar ve döner: önce üst sınıf yazdı.
Kedi yapıcısı kaldığı yerden devam eder: evcil = true.
Kedi yapıcısı mesajını yazar.
Yapıcı bitti; nesnenin adresi kedi değişkenine atanır.
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
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.
Yapıcılar ve super · Adım adım kod izleme
this(...) ve super(...): Kod İzleme
new önce nesneye yer ayırır; üç alan da varsayılan değerdedir. Ardından Yonetici(String) çağrılır.
İ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.
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.
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.
İ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.
İki parametreli yapıcı döndü; Calisan(String) kendi satırını yazar.
super(ad) tamamlandı. Ancak şimdi alt sınıfın kendi alanı dolar: prim = 5000.0.
En son, ilk çağrılan Yonetici(String) biter. Dört yapıcı çalıştı ama tek bir nesne kuruldu.
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
İ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.
Sonra Alt'ın static bloğu. Sınıf başlatma program boyunca bir kez yapılır.
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(...).
Ardından Ust() gövdesi. Bu anda b hâlâ 0: Alt kısmı henüz hiç ilk değer almadı.
super() döndükten sonra alt sınıfın alan ilk değerleri çalışır: b = deger("Alt alanı").
En son Alt() gövdesi. Sıra: üst sınıf alanları → üst yapıcı → alt sınıf alanları → alt yapıcı.
İ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:
publicmetotpublickalmalı. final,staticveprivatemetotlar 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çin | Aynı sınıfta (veya alt sınıfta) |
| Ad | Aynı | Aynı |
| Parametreler | Aynı | Farklı (sayı ya da tip) |
| Dönüş tipi | Aynı veya alt tipi | Serbest |
| Amaç | Kalıtımla alınan davranışı değiştirmek | Aynı 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
@Overridebir 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ı
| Özellik | Alt sınıftaki metotta izin verilen | Kural bozulursa javac |
|---|---|---|
| Ad ve parametreler | Birebir aynı | Hata yok: yeni bir metot (aşırı yükleme) olur |
| Dönüş tipi | Aynı ya da bir alt tipi (kovaryant) | return type String is not compatible with Hayvan |
| Erişim | Aynı ya da daha geniş: protected → public | attempting to assign weaker access privileges |
| Kontrol edilen istisna | Aynısı, daha özeli ya da hiç | overridden method does not throw Exception |
static | Örnek metodu static olamaz, static metot örnek metodu olamaz | overriding method is static / overridden method is static |
final metot | Geçersiz kılınamaz | overridden method is final |
private metot | Görünmez; aynı adlı metot ilgisiz yeni bir metottur | Hata yok (@Override varsa hata) |
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).
Kedireferansıyla çağıran, sonucu doğrudanKediolarak alır;(Kedi)dönüşümüne gerek kalmaz.Hayvanreferansıyla çağıran için söz bozulmaz: birKedide birHayvan'dır.- Yalnızca referans tiplerinde geçerlidir;
intyerinelongdö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
privatebir 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çindekiselam()çağrısı derleme sırasındaUst'un kendiprivatemetoduna bağlanır. Nesne birAltolsa daUst.selamyazılır.@Overridealışkanlığı bu yanılgıyı derleme anında ortaya çıkarır.
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)
GidaUrunuyalnızcakdvOrani()'nı değiştirir;satisFiyati()vetoString()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ıf | Aynı paket | Alt 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
privatealanlarına alt sınıf, yalnızca üst sınıftan kalıtımla alınanpublicveyaprotectedmetotlar 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:isimvek.isimgeçerlidir. hise herhangi birHayvan'ı, örneğin birKopek'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
finalanahtar sözcüğü kalıtımı sınırlamak için kullanılabilir.- Bir metot
finaltanımlanırsa hiçbir alt sınıfta geçersiz kılınamaz (override). - Bir sınıf
finaltanı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
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 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 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.
// 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
new Araba(90): tek nesne, içinde üst sınıf Arac'tan gelen hiz ve kendi motor alanı var (is-a).
Yapıcı ikinci bir nesne oluşturur: araba bir motor değildir, motora sahiptir (has-a). Bağlantı bir referanstır.
yolaCik() Araba nesnesi üzerinde çalışır.
motor.calistir(): Araba işi kendi yapmaz, referansı izleyip Motor nesnesine devreder.
hizlan(50) Araba'da yazılmadı; Arac'tan kalıtımla gelir ve aynı nesnenin hiz alanını 50 yapar.
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).
class D extends B, C { } // geçersiz
error: '{' expectedinterface 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
“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.
Üst sınıfın alanlarına super(...) ve üst sınıfın metotlarıyla değer verilir; alanlar private tutulur.
toString ve equals (ve onunla birlikte hashCode) her anlamlı sınıfta geçersiz kılınmalıdır.
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şki | is-a: “her X bir Y'dir” | has-a: “X'in bir Y'si vardır” |
| Bağlılık | Sıkı: alt sınıf, üst sınıfın gerçeklemesine de bağlıdır | Gevşek: yalnızca içteki nesnenin public metotlarına bağlıdır |
| Yeniden kullanım | Otomatik: 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 sabittir | Alan, çalışma zamanında başka bir nesneyle değiştirilebilir |
| Üst tip yerine kullanım | Doğrudan: Hayvan h = new Kedi() | Ortak bir arayüz uygulanırsa |
is-a ilişkisi gerçekten doğruysa ve üst sınıf genişletilmek üzere tasarlanıp belgelendiyse: Kedi – Hayvan.
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
SayanListe, eklenen öğe sayısını tutmak için Liste'den türetildi. İki ekleme metodu da geçersiz kılındı.
Alt sınıfın hepsiniEkle'si sayaca dizinin uzunluğunu ekler: 3. Sonra işi üst sınıfa bırakı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.
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.
super.ekle(s) öğeyi diziye yazar: boyut = 1.
"b" ve "c" için de aynı yol izlenir: her biri bir kez daha sayılır. Döngü bitince sayaç 6, boyut 3.
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, birListe'ye sahiptir. Her metot önce kendi işini yapar, sonra çağrıyı içteki nesneye iletir (forwarding). liste.hepsiniEkle(...)içindekiekleçağrıları düz birListenesnesinde ç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ızcapublicmetotlaradır.- Bedeli:
SayanListe2birListebeklenen 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
Dikdortgensınıfı “en ve boy birbirinden bağımsız değişir” sözünü verir;Karebu sözü tutamaz. genislethatalı değildir; hata, sözü bozan alt sınıftadır.- Çözümler:
Kare'yiDikdortgen'den türetmemek (ikisi deSekil'den türer) ya da sınıfları değiştirilemez yapmak.
Object Sınıfı
Object Sınıfı
- Her sınıf
java.lang.Objectsınıfından türer;extendsyazı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 { }
| Metot | Varsayı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"..." + nesneonu 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ı?
new bir nesne oluşturur; k1 onun adresini (referansını) tutar.
İkinci new: içeriği aynı, ama ayrı bir nesne. k2 onu gösterir.
k3 = k1 nesne kopyalamaz; yalnızca adresi kopyalar. k1 ve k3 aynı nesneyi gösterir.
== referansları karşılaştırır: k1 ve k2 farklı nesneleri gösterir → false.
k1 ve k3 aynı nesneyi gösterir → 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
truedönmelidir. - Parametre
Objecttipindedir; hatalı durumları engellemek için önce tip kontrolü yapılmalıdır.
equals sözleşmesi
| Yansıma | x.equals(x) → true |
| Simetri | x.equals(y) ⇔ y.equals(x) |
| Geçişme | x = y ve y = z ise x = z |
| Tutarlılık | Alanlar değişmedikçe sonuç değişmez |
| null | x.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ı
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
Objectolmalı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 hementrue(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)equalsile karşılaştırılır.
| Tip kontrolü | Alt sınıf nesnesi eşit sayılabilir mi? |
|---|---|
instanceof | Evet (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
Üç ayrı nesne var. k1 ile k2 içerikçe aynı, k3 yalnızca yaşta farklı.
k1.equals(k2): yeni çerçevede this = #1, obj = #2. obj'nin tipi Object.
this == obj adresleri karşılaştırır: #1 ≠ #2, hızlı yol işlemez.
obj null değil; getClass() iki tarafta da Kisi. Koşul yanlış, return false atlanır.
Aşağı dönüşüm yeni nesne üretmez: kisi aynı #2'yi, bu kez Kisi tipiyle gösterir; artık kisi.yas yazılabilir.
İlkel yas == ile, String olan ad equals ile karşılaştırılır. İkisi de doğru: true.
k1.equals(k3): yeni çağrı, obj = #3. Farklı nesne, aynı sınıf: alanlara geçilir.
20 == 21 yanlış; && kısa devre yapar, ad.equals hiç çağrılmaz. Sonuç false.
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
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 equals | 2 false ✗ |
equals + hashCode | 1 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
Boş bir HashSet: öğeler 16 kovadan birine konur. Kovayı hashCode() seçer.
hashCode() = 31·(31+3)+4 = 1058; 1058 % 16 = 2. Kova boş: nesne eklenir.
Aynı sayılar, farklı sıra: hash 1088, kova 0. Yine boş kova, eklenir.
Yeni bir Nokta(3, 4) nesnesi: hash yine 1058, aynı kova 2. Kova dolu: içindekiyle equals sorulur.
equals doğru: küme aynı öğeyi ikinci kez almaz, add false döndürür.
Kümede iki öğe var: 2.
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ım | class 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ınmayan | Yapıcılar; private üyeler nesnede vardır ama doğrudan erişilemez |
super | super(...): üst sınıf yapıcısı (ilk satır); super.m(): üst sınıfın sürümü |
| Yapıcı zinciri | Object → … → Ust → Alt sırasıyla tamamlanır |
| Geçersiz kılma | Aynı imza, alt sınıfta yeni gövde; @Override ile derleyici denetler |
protected | Sınıf + alt sınıflar + aynı paket |
final | Metot: geçersiz kılınamaz; sınıf: alt sınıfı olamaz |
| Has-a | Oluşum: başka sınıf tipinde alan; çoklu kalıtım yerine oluşum ve arayüzler |
| Object | Her sınıfın atası; toString, equals, hashCode geçersiz kılınır |
== / equals | Referans karşılaştırma / içerik karşılaştırma |
Özet
Sık Yapılan Hatalar
Üst sınıfta parametresiz yapıcı yoksa: constructor Hayvan in class Hayvan cannot be applied to given types.
saglik += 5; alt sınıfta: saglik has private access in Hayvan. Çözüm: getter ya da protected metot.
Parametre Object değilse aşırı yüklemedir; @Override ile: method does not override or implement a method from a supertype.
İçerikleri aynı iki nesne için == → false. String dahil, içerik equals ile karşılaştırılır.
Derleme hatası yoktur ama HashSet/HashMap yanlış çalışır: aynı nokta iki kez eklenir.
class D extends B, C → '{' expected. Çözüm: oluşum ya da arayüz.
public void ciz() alt sınıfta void ciz() olursa: attempting to assign weaker access privileges.
(Lastik) p, p bir Lastik değilse: ClassCastException. Önce instanceof.
Özet
Sık Yapılan Hatalar (2)
İkisi de ilk satır olmak ister: call to super must be first statement in constructor. Zincirin sonundaki yapıcı super(...) çağırır.
String yavrula(): return type String is not compatible with Hayvan. Yalnızca alt tip (kovaryant) olabilir.
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.
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.
void f() throws Exception, üstte throws yoksa: overridden method does not throw Exception.
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.
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.
Ü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"); } }
C c = new C();ne yazdırır?- Ardından
c.yaz();ne yazdırır? A a = new B(); a.yaz();ne yazdırır?c instanceof Anedir?equals'ı geçersiz kılmayan birOgrencisınıfında, aynı adlı iki nesne içino1.equals(o2)nedir?
Cevapları göster
- A B C: yapıcılar üstten alta tamamlanır (B'nin gizli
super()'ı önce A'yı çalıştırır). - B.yaz, sonra C.yaz:
super.yaz()B'nin sürümünü çağırır. - A B, sonra B.yaz: değişken A tipinde ama nesne B olduğu için B'nin sürümü çalışır.
- true: C, B üzerinden A'dan türer.
- 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 + " "); } }
- Programdaki ilk
new B();ne yazdırır? (Süslü parantezli satırlar örnek ilklendirici bloklarıdır.) - Ardından
new B(7);ne yazdırır? - Yerine koyma örneğindeki
KareileDikdortgen d = new Kare(); d.setBoy(4); d.setEn(5);sonrasındad.alan()kaçtır? Ust'teprotected Object kopya()var.Alt extends Ustiçinde@Override public Alt kopya()derlenir mi?
Cevapları göster
- 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çinB'nin örnek bloğu (Bb) yalnızca bir kez,B(int)içindesuper()döndükten sonra çalışır. - Ab A Bb B7: static bloklar tekrar çalışmaz.
- 25: son çağrı
setEn(5);Kareiki kenarı da 5 yapar. - Evet: erişim genişler (
protected→public) veAlt,Object'in alt tipidir (kovaryant dönüş).
Özet
Alıştırmalar
Calisan(ad,maas) ve ondan türeyenYonetici(prim) yazın;toplamGelir()vesuper.toString()kullanan birtoString()ekleyin.Hayvan'ın tek yapıcısıHayvan(String isim).Kus(String isim, boolean ucabilir)yapıcısını yazın.Ogrenci(int no,String ad) içinequalsvehashCodeyazın.- Başka paketteki, alt sınıf olmayan bir sınıf
protectedbir alana erişebilir mi? - is-a mı, has-a mı? Öğrenci–Kişi, Araba–Tekerlek, Kütüphane–Kitap, Kare–Şekil.
Cevaplar / ipuçları
- Yapıcı:
super(ad, maas); this.prim = prim;·toplamGelir():getMaas() + prim.new Yonetici("Ayşe", 50000, 8000)→ 58000.0. Kus(String isim, boolean ucabilir) { super(isim); this.ucabilir = ucabilir; }if (!(o instanceof Ogrenci)) return false; Ogrenci d = (Ogrenci) o; return no == d.no && ad.equals(d.ad);·return Objects.hash(no, ad);- Hayır: protected = sınıf + aynı paket + alt sınıflar.
- 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).