Eğitim Portalı/Java/Kalıtım ve Çok Biçimlilik
Java01-java/04-kalitim-ve-cok-bicimlilik

Kalıtım ve Çok Biçimlilik

OOP'nin temellerinde nesneleri sınıflarla modellemeyi gördük. Şimdi iki güçlü mekanizmaya geçiyoruz: kalıtım sayesinde var olan bir sınıfın özelliklerini yeniden kullanır, çok biçimlilik sayesinde farklı nesneleri tek…

Kalıtım ve Çok Biçimlilik

OOP'nin temellerinde nesneleri sınıflarla modellemeyi gördük. Şimdi iki güçlü mekanizmaya geçiyoruz: kalıtım sayesinde var olan bir sınıfın özelliklerini yeniden kullanır, çok biçimlilik sayesinde farklı nesneleri tek bir tip üzerinden, ortak bir dille yönetiriz. Bu ikisi, esnek ve genişletilebilir tasarımın anahtarıdır.

Kalıtım (inheritance)

Birbirine benzeyen sınıflar yazarken aynı kodu tekrarlamak istemeyiz. Ortak alan ve davranışları bir üst sınıfta toplar, özel olanları alt sınıflarda tanımlarız. Alt sınıf, üst sınıfı extends ile genişletir ve onun her şeyini miras alır:

class Hayvan {
    protected String ad;
    void nefesAl() { ... }
}
class Kopek extends Hayvan {
    Kopek(String ad, int yas) { super(ad, yas); }
}

super, üst sınıfa erişmenin yoludur: super(...) üst sınıfın constructor'ını çağırır, super.metot() ise üst sınıftaki bir metodu çağırır. protected erişim belirleyicisi, bir alanın alt sınıflar tarafından görülebilmesini sağlar.

Metot ezme (override) ve Object metotları

Alt sınıf, miras aldığı bir metodu kendi ihtiyacına göre yeniden yazabilir; buna override denir ve @Override ile işaretleriz. Java'daki her sınıf gizlice Object'ten türer; en sık ezdiğimiz Object metodu toString()'tir. Onu ezdiğinde, nesneyi yazdırdığında okunabilir bir metin alırsın:

@Override
public String toString() {
    return "Kopek{ad=" + ad + "}";
}

Örnek 1 (./Ornek1.java) Hayvan üst sınıfından Kopek ve Kedi türetir; super ile constructor çağırır, sesCikar() metodunu ezer ve toString() ile anlamlı çıktı üretir.

Çok biçimlilik (polymorphism)

Çok biçimlilik, "tek arayüz, çok davranış" demektir. Bir üst tip referansıyla farklı alt tipleri tutabilirsin; bir metodu çağırdığında ise nesnenin gerçek tipine ait sürüm çalışır. Buna dinamik bağlama denir:

List<Sekil> sekiller = List.of(new Daire(3), new Kare(4));
for (Sekil s : sekiller) {
    s.alan(); // Daire için daire alanı, Kare için kare alanı çalışır
}

Burada döngü Sekil ile konuşur ama her nesne kendi alan() hesabını yapar. Yeni bir şekil eklemek istediğinde döngüyü değiştirmen gerekmez — yalnızca yeni bir sınıf eklersin. Örnek 2 (./Ornek2.java) şekillerin alanını çok biçimli biçimde toplar.

instanceof ve pattern matching

Bazen elindeki üst tip referansının hangi alt tip olduğunu öğrenmen gerekir. instanceof bunu kontrol eder; modern pattern matching (Java 16+) ise kontrol ve dönüştürmeyi tek adımda yapıp otomatik bir değişken tanımlar:

if (o instanceof KrediKarti kk) {
    // kk burada doğrudan KrediKarti tipinde kullanılabilir
    System.out.println(kk.kartNo());
}

equals ve hashCode

== iki referansın aynı nesne olup olmadığına bakar. Çoğu zaman istediğimiz ise mantıksal eşitlik: "değerleri aynı mı?". Bunun için equals() (ve onunla uyumlu hashCode()) metotlarını ezeriz. Objects.hash(...) ve Objects.equals(...) bu işi güvenli ve kısa hale getirir. Örnek 3 (./Ornek3.java) ödeme türlerinde pattern matching'i ve değer bazlı eşitliği gösterir.

Özet

Kalıtımla kodu yeniden kullandık, super ve @Override ile üst/alt sınıf ilişkisini yönettik. Çok biçimlilikle farklı nesneleri tek tip üzerinden çalıştırdık; instanceof pattern matching ve equals/hashCode ile tip ve eşitlik denetimini öğrendik. Sırada, "ne yapılacağını" tanımlayıp "nasıl"ı alt sınıflara bırakan soyutlama var.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Kalıtım (extends), super ve toString() override.
2// Çalıştırma: java Ornek1.java
3public class Ornek1 {
4
5    public static void main(String[] args) {
6        Kopek k = new Kopek("Karabaş", 3);
7        Kedi c = new Kedi("Tekir", 2);
8
9        // Alt sınıflar, üst sınıftaki ortak davranışı (nefesAl) miras alır.
10        k.nefesAl();
11        k.sesCikar();
12        c.sesCikar();
13
14        // toString override edildiği için nesneyi yazdırınca anlamlı metin görürüz.
15        System.out.println(k);
16        System.out.println(c);
17    }
18}
19
20// Üst (ana/base) sınıf: ortak alan ve davranışlar burada.
21class Hayvan {
22    protected String ad;   // protected: alt sınıflar erişebilir
23    protected int yas;
24
25    public Hayvan(String ad, int yas) {
26        this.ad = ad;
27        this.yas = yas;
28    }
29
30    public void nefesAl() {
31        System.out.println(ad + " nefes alıyor.");
32    }
33
34    public void sesCikar() {
35        System.out.println(ad + " bir ses çıkardı.");
36    }
37
38    @Override
39    public String toString() {
40        return getClass().getSimpleName() + "{ad=" + ad + ", yas=" + yas + "}";
41    }
42}
43
44// Kopek, Hayvan'ı genişletir (extends): onun her şeyini miras alır.
45class Kopek extends Hayvan {
46    public Kopek(String ad, int yas) {
47        super(ad, yas); // üst sınıfın constructor'ını çağırır
48    }
49
50    @Override
51    public void sesCikar() {
52        System.out.println(ad + ": Hav hav!");
53    }
54}
55
56class Kedi extends Hayvan {
57    public Kedi(String ad, int yas) {
58        super(ad, yas);
59    }
60
61    @Override
62    public void sesCikar() {
63        System.out.println(ad + ": Miyav!");
64    }
65}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Çok biçimlilik (polymorphism) ve dinamik bağlama.
2// Çalıştırma: java Ornek2.java
3import java.util.List;
4
5public class Ornek2 {
6
7    public static void main(String[] args) {
8        // Üst tip (Sekil) referansıyla farklı alt tipleri tutabiliriz.
9        // Bu, çok biçimliliğin temelidir: tek tip, çok davranış.
10        List<Sekil> sekiller = List.of(
11                new Daire(3),
12                new Kare(4),
13                new Daire(1.5),
14                new Kare(2.5)
15        );
16
17        double toplamAlan = 0;
18        for (Sekil s : sekiller) {
19            // s.alan() çağrısı, nesnenin GERÇEK tipine göre çözülür (dinamik bağlama).
20            System.out.printf("%-6s alan = %.2f%n", s.ad(), s.alan());
21            toplamAlan += s.alan();
22        }
23        System.out.printf("%nToplam alan = %.2f%n", toplamAlan);
24    }
25}
26
27abstract class Sekil {
28    abstract double alan();      // her şekil kendi alanını farklı hesaplar
29    abstract String ad();
30}
31
32class Daire extends Sekil {
33    private final double yaricap;
34    Daire(double yaricap) { this.yaricap = yaricap; }
35
36    @Override double alan() { return Math.PI * yaricap * yaricap; }
37    @Override String ad() { return "Daire"; }
38}
39
40class Kare extends Sekil {
41    private final double kenar;
42    Kare(double kenar) { this.kenar = kenar; }
43
44    @Override double alan() { return kenar * kenar; }
45    @Override String ad() { return "Kare"; }
46}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: instanceof pattern matching ve equals/hashCode.
2// Çalıştırma: java Ornek3.java
3import java.util.Objects;
4
5public class Ornek3 {
6
7    public static void main(String[] args) {
8        Odeme[] odemeler = {
9                new KrediKarti("1234", 150.0),
10                new Havale("TR55", 300.0),
11                new KrediKarti("9999", 75.0)
12        };
13
14        // instanceof pattern matching (Java 16+):
15        // tip kontrolü ve dönüştürme tek adımda; ayrıca otomatik değişken tanımlanır.
16        for (Odeme o : odemeler) {
17            if (o instanceof KrediKarti kk) {
18                System.out.println("Kredi kartı (" + kk.kartNo() + ") -> " + kk.tutar() + " TL");
19            } else if (o instanceof Havale h) {
20                System.out.println("Havale (" + h.iban() + ") -> " + h.tutar() + " TL");
21            }
22        }
23
24        // equals/hashCode: iki nesnenin "mantıksal eşitliğini" tanımlar.
25        KrediKarti a = new KrediKarti("1234", 150.0);
26        KrediKarti b = new KrediKarti("1234", 150.0);
27        System.out.println("\na == b ?      " + (a == b));        // false: farklı nesneler
28        System.out.println("a.equals(b) ? " + a.equals(b));       // true: aynı değerler
29        System.out.println("hashCode eşit ? " + (a.hashCode() == b.hashCode()));
30    }
31}
32
33abstract class Odeme {
34    private final double tutar;
35    Odeme(double tutar) { this.tutar = tutar; }
36    double tutar() { return tutar; }
37}
38
39class KrediKarti extends Odeme {
40    private final String kartNo;
41    KrediKarti(String kartNo, double tutar) { super(tutar); this.kartNo = kartNo; }
42    String kartNo() { return kartNo; }
43
44    @Override
45    public boolean equals(Object o) {
46        if (this == o) return true;
47        if (!(o instanceof KrediKarti other)) return false;
48        return Double.compare(tutar(), other.tutar()) == 0 && kartNo.equals(other.kartNo);
49    }
50
51    @Override
52    public int hashCode() {
53        return Objects.hash(kartNo, tutar());
54    }
55}
56
57class Havale extends Odeme {
58    private final String iban;
59    Havale(String iban, double tutar) { super(tutar); this.iban = iban; }
60    String iban() { return iban; }
61}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.