---
title: 'Kalıtım ve Yeniden Tanımlama'
source: 'https://academia.sh/tr/kurslar/java-nesne-modeli/kalitim-ve-yeniden-tanimlama'
course: 'Nesneye Dayalı Java'
language: tr
updated: '2026-08-17T18:09:41+00:00'
license: 'CC BY-SA 4.0'
---

# Kalıtım ve Yeniden Tanımlama

Tek bir referans üzerinde sekiz karar okunuyor ve yanıt yarı yarıya bölünüyor: dördünü çalışma zamanı tipi, dördünü bildirilen tip veriyor. Alanların aynı adla yeniden bildirilmesi yeniden tanımlama değil gizlemedir; imza değiştiğinde ise yeniden tanımlama hiç doğmaz ve karar taraf değiştirir.

İki derstir kararın bir tarafı ayrı ayrı ölçüldü. Yapıcıdan çağrılan yeniden tanımlı yöntemi
**çalışma zamanı tipi** seçti; bir üyenin görülüp görülmediğine **bildirilen tip** karar
verdi. İkisi hiç yan yana konmadı, dolayısıyla aradaki kuralın ne olduğu da sorulmadı.

Bu ders kursun çekirdek ölçümüdür. Tek bir referans kurulur — bildirilen tipi `Ust`, çalışma
zamanı tipi `Alt` — ve o referans üzerinden sekiz ayrı dilsel karar okunur. Her karar için tek
soru sorulur: sonucu hangi tip verdi? Ölçümün kâhini kurgunun kendisidir; iki tipi bilerek
ayırdığımız için her yanıtın kaynağı baştan bellidir.

Programlama Paradigmaları kursu kalıtımın iki vaadini ve alt tip sözleşmesinin nasıl ihlal
edildiğini ölçmüştü; orada sayılan şey **sözleşme ihlaliydi**. Burada sayılan şey **kararın
tarafıdır** ve ölçüm çalıştırılmış bir koşumdan gelir.

## Ölçüm Çekirdeği

Çekirdek üç tipten oluşur: bir arayüz (`Tasiyici`), bir üst sınıf (`Ust`) ve onu genişleten
bir alt sınıf (`Alt`). `Alt`, `Ust`'un yöntemini **yeniden tanımlar**, alanını ise aynı adla
**yeniden bildirir** — ikisinin ayrı şeyler olduğu bu dersin ikinci ölçümüdür.

- **SN11** — Her üye kendi kaynağını metin olarak döndürür (`ust alan`, `alt yontem`); dönen
  değer hangi bildirimden geldiğini kendisi söyler, çıkarım gerekmez.
- **SN12** — Sekiz kararın hepsi **aynı** referans üzerinden okunur; değişen tek şey okunan
  üyenin cinsidir.
- **SN13** — Ölçüm ortama bağlı hiçbir veri okumaz: kimlik karması, süre ve yol yazılmaz;
  kimlik gerekirse `==` ile karşılaştırılır.

```java
// Ust.java — kursun çekirdeği: bir arayüz, bir üst sınıf, onu genişleten bir alt sınıf
interface Tasiyici {
    default String tasi() { return "arayuz varsayilani"; }
}

class Ust implements Tasiyici {
    String etiket = "ust alan";
    String ad() { return "ust yontem"; }
    String karsila(Ust u) { return "Ust.karsila(Ust)"; }
    static String durum() { return "ust statik"; }
    @Override public String toString() { return "Ust nesnesi"; }
}

class Alt extends Ust {
    String etiket = "alt alan";
    @Override String ad() { return "alt yontem"; }
    String karsila(Alt a) { return "Alt.karsila(Alt)"; }
    static String durum() { return "alt statik"; }
    @Override public String tasi() { return "alt yeniden tanim"; }
    @Override public String toString() { return "Alt nesnesi"; }
}
```

Derste iki ölçüm koşum değil **derleme** okur: birinde bir kaynağın kabul edilip edilmediği,
ötekinde üretilen sınıf dosyasındaki alanlar sayılır. İkisi de aynı yardımcıdan gelir.

```java
// Derle.java — kaynağı derler; ya derleme sonucunu ya da bir sınıfın alanlarını verir
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Derle {
    static Path derle(String kaynak) throws Exception {
        Path d = Files.createTempDirectory("kalitim");
        Path k = d.resolve("Deneme.java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        return ToolProvider.findFirst("javac").orElseThrow()
                .run(bos, bos, "-d", d.toString(), k.toString()) == 0 ? d : null;
    }

    static String olur(String kaynak) throws Exception {
        return derle(kaynak) == null ? "derlenmedi" : "derlendi";
    }

    static List<String> alanlar(String kaynak, String sinif) throws Exception {
        Path d = derle(kaynak);
        List<String> ad = new ArrayList<>();
        for (FieldModel f : ClassFile.of().parse(d.resolve(sinif + ".class")).fields())
            ad.add(f.fieldName().stringValue());
        return ad;
    }
}
```

## Sekiz Karar, Tek Referans

```java
// Karar.java — tek referans üzerinde sekiz karar
public class Karar {
    static String secim(Ust u) { return "Ust imzasi"; }
    static String secim(Alt a) { return "Alt imzasi"; }

    static void bildir(String karar, String sonuc, String veren) {
        System.out.printf("%-24s %-22s %s%n", karar, sonuc, veren);
    }

    public static void main(String[] args) {
        Ust d = new Alt();
        System.out.println("bildirilen tip Ust, calisma zamani tipi "
                + d.getClass().getSimpleName());
        System.out.println();
        System.out.printf("%-24s %-22s %s%n", "karar", "sonuc", "karari veren");
        bildir("yeniden tanimli yontem", d.ad(), "calisma zamani tipi");
        bildir("arayuz varsayilani", d.tasi(), "calisma zamani tipi");
        bildir("metne cevirme", d.toString(), "calisma zamani tipi");
        bildir("alan okumasi", d.etiket, "bildirilen tip");
        bildir("asiri yukleme secimi", secim(d), "bildirilen tip");
        bildir("statik yontem", Ust.durum(), "bildirilen tip");
        bildir("tip sinamasi", String.valueOf(d instanceof Alt), "calisma zamani tipi");
        bildir("asagi donusum", ((Alt) d).etiket, "bildirilen tip (donusum sonrasi)");
    }
}
```

```
bildirilen tip Ust, calisma zamani tipi Alt

karar                    sonuc                  karari veren
yeniden tanimli yontem   alt yontem             calisma zamani tipi
arayuz varsayilani       alt yeniden tanim      calisma zamani tipi
metne cevirme            Alt nesnesi            calisma zamani tipi
alan okumasi             ust alan               bildirilen tip
asiri yukleme secimi     Ust imzasi             bildirilen tip
statik yontem            ust statik             bildirilen tip
tip sinamasi             true                   calisma zamani tipi
asagi donusum            alt alan               bildirilen tip (donusum sonrasi)
```

Sekiz kararın **dördü** çalışma zamanı tipinden, **dördü** bildirilen tipten geliyor. "Java
nesneye dayalıdır, çağrı gerçek nesneye gider" cümlesi kararların yalnız yarısını açıklar.

Ayrım rastgele değildir. Üstteki üç satır ile yedinci satır **çalışmayı** ilgilendirir:
hangi gövdenin yürütüleceği, nesnenin gerçekten ne olduğu. Bunlar çalışma zamanı tipine
bakar, çünkü yanıt ancak nesne ortadayken bilinebilir. Kalan dördü **adı çözme** işidir:
hangi alan okunacak, hangi imza seçilecek, hangi sınıfın statik yöntemi çağrılacak. Bunlar
derleyicinin masasında biter ve derleyicinin elinde yalnız bildirilen tip vardır.

Kural tek cümlede toplanabilir: **çağrılan davranış çalışma zamanı tipine, seçilen ad
bildirilen tipe bakar.** İkinci satır bunun en görünmez örneğidir — arayüzden gelen bir
varsayılan yöntem de yeniden tanımlanabilir ve kararı yine çalışma zamanı tipi verir.

Son satır dördüncü bir okuma ekliyor. `((Alt) d).etiket` ifadesi **alt alan** veriyor, oysa
nesne baştan beri aynı nesnedir. Aşağı dönüşüm nesneyi değiştirmez, **bildirilen tipi**
değiştirir; derleyiciye verilen bir sözdür ve sözün doğruluğu çalışma zamanında sınanır.

## Yeniden Tanımlama ile Gizlemenin Ayrımı

Dördüncü satır dersin en sessiz sonucudur. `d.ad()` **alt yontem** verirken `d.etiket` **ust
alan** veriyor; ikisi de aynı `d` üzerinden, iki karakterlik bir fark ile yazılıyor. Alanların
aynı adla yeniden bildirilmesi yeniden tanımlama değildir; **gizlemedir**. Gizlenen alanın ne
olduğunu görmek için tek nesneye iki ayrı referans bağlanır.

```java
// Gizleme.java — gizlenen alan yok olmuyor
public class Gizleme {
    public static void main(String[] args) {
        Ust u = new Alt();
        Alt a = (Alt) u;
        System.out.println("iki referans ayni nesne mi : " + (u == a));
        System.out.println("Ust referansindan etiket   : " + u.etiket);
        System.out.println("Alt referansindan etiket   : " + a.etiket);
        System.out.println("Ust referansindan ad()     : " + u.ad());
        System.out.println("Alt referansindan ad()     : " + a.ad());
        a.etiket = "yazildi";
        System.out.println("a.etiket yazildiktan sonra u.etiket: " + u.etiket);
    }
}
```

```
iki referans ayni nesne mi : true
Ust referansindan etiket   : ust alan
Alt referansindan etiket   : alt alan
Ust referansindan ad()     : alt yontem
Alt referansindan ad()     : alt yontem
a.etiket yazildiktan sonra u.etiket: ust alan
```

İlk satır ölçümün dayanağıdır: ortada tek bir nesne vardır. Buna karşın o tek nesne `etiket`
sorusuna **iki ayrı yanıt** veriyor, `ad()` sorusuna **tek yanıt**. Yeniden tanımlanan yöntem
üsttekinin yerini alır; yeniden bildirilen alan üsttekinin yerini almaz, **üstüne konur.**

Son satır bunun bellek karşılığıdır. `a.etiket` alanına yazıldıktan sonra `u.etiket` hâlâ
**ust alan** diyor. Aynı nesnenin içinde iki ayrı yer vardır ve biri yazıldığında öteki
değişmez. Gizlenen alan yok olmamıştır; yalnız bildirilen tipi `Alt` olan bir referanstan
görünmez olmuştur.

İki yerin gerçekten iki yer olduğu sınıf dosyasından da okunabilir. Aynı ikili derlenir ve
her iki sınıfın bildirdiği alanlar listelenir.

```java
// Yer.java — gizlenen alan sınıf dosyasında nerede duruyor
public class Yer {
    static final String KAYNAK = """
        class Ust { String etiket = "ust alan"; String ad() { return "ust yontem"; } }
        class Alt extends Ust { String etiket = "alt alan";
                                @Override String ad() { return "alt yontem"; } }
        """;

    public static void main(String[] args) throws Exception {
        System.out.println("Ust sinif dosyasindaki alanlar: " + Derle.alanlar(KAYNAK, "Ust"));
        System.out.println("Alt sinif dosyasindaki alanlar: " + Derle.alanlar(KAYNAK, "Alt"));
    }
}
```

```
Ust sinif dosyasindaki alanlar: [etiket]
Alt sinif dosyasindaki alanlar: [etiket]
```

İki ayrı sınıf dosyası, her birinde bir `etiket` bildirimi. Yeniden tanımlanan `ad()` yöntemi
için de iki gövde vardır, ama çağrı yalnız birine gider; alanda böyle bir seçim yoktur, ikisi
de okunabilir durumdadır. Kalıtım yöntemde **birini ötekinin yerine koyar**, alanda **ikisini
yan yana bırakır**.

Bedeli buradan okunur. Bir alt sınıfa üstündekiyle aynı adı taşıyan bir alan yazan
programcı, üstteki alanı değiştirdiğini düşünür. Değiştirmemiştir — iki alan da yaşamaya
devam eder ve hangisinin okunacağı, o satırdaki referansın bildirilen tipine bakılarak
belirlenir. Kusur yazan yerde görünmez; okuyan yerde, hem de yanlış değeri sessizce
döndürerek görünür.

## Derleyicinin Yakaladığı ve Kaçırdığı

Yeniden tanımlama bir bildirim değildir; iki imza denk düştüğünde kendiliğinden doğar. Bu
yüzden **doğmaması** da kendiliğinden olur ve sessizdir. `@Override` işareti tam olarak bu
sessizliği bozmak içindir: alt sınıfa "bu yöntem gerçekten bir yeniden tanımlama mı" sorusunu
derleyiciye sordurur.

- **SN14** — Beş denemede de üst sınıf birebir aynıdır; değişen tek şey alt sınıfın tek
  satırlık bildirimidir. Ölçülen yalnız derleme sonucudur.

```java
// Isaret.java — @Override ne yakalıyor, imza ne kadar oynayabiliyor
public class Isaret {
    static final String TABAN = """
        class A { String ad() { return "ust"; } A uret() { return this; }
                  String karsila(A a) { return "A"; } }
        """;

    static final String[][] DENEME = {
        { "isaretsiz, ad yanlis yazilmis", "class B extends A { String adi() { return \"alt\"; } }" },
        { "isaretli, ad yanlis yazilmis", "class B extends A { @Override String adi() { return \"alt\"; } }" },
        { "isaretli, donus tipi daraltilmis", "class B extends A { @Override B uret() { return this; } }" },
        { "isaretli, donus tipi genisletilmis", "class B extends A { @Override Object uret() { return this; } }" },
        { "isaretli, parametre tipi daraltilmis", "class B extends A { @Override String karsila(B b) { return \"B\"; } }" },
    };

    public static void main(String[] args) throws Exception {
        System.out.printf("%-38s %s%n", "alt sinif", "sonuc");
        for (String[] d : DENEME)
            System.out.printf("%-38s %s%n", d[0], Derle.olur(TABAN + d[1] + "\n"));
    }
}
```

```
alt sinif                              sonuc
isaretsiz, ad yanlis yazilmis          derlendi
isaretli, ad yanlis yazilmis           derlenmedi
isaretli, donus tipi daraltilmis       derlendi
isaretli, donus tipi genisletilmis     derlenmedi
isaretli, parametre tipi daraltilmis   derlenmedi
```

İlk iki satır işaretin bütün değerini taşıyor. Adı yanlış yazılmış bir yöntem, işaret
konmadığında **derleniyor**: derleyici için ortada bir kusur yoktur, alt sınıfa yeni bir
yöntem eklenmiştir. Program çalışır, hiçbir uyarı çıkmaz ve üst sınıfın sürümü çağrılmaya
devam eder. Aynı kaynak `@Override` ile **derlenmiyor**. İşaret bir davranış eklemez; yalnız
bir varsayımı derleyiciye bildirir ve varsayım tutmadığında kaynağı reddettirir.

Üçüncü ile dördüncü satır dönüş tipinin ne kadar oynayabildiğini ölçüyor. Yeniden tanımlayan
yöntem dönüş tipini **daraltabilir** — üstteki `A` döndürüyorsa alttaki `B` döndürebilir — ama
genişletemez. Nedeni alt tip sözleşmesidir: `A` bekleyen çağıran taraf bir `B` aldığında
sözleşme bozulmaz, ama `Object` aldığında bozulur. Daraltma çağırana verilen sözü güçlendirir,
genişletme zayıflatır.

Beşinci satır sıradaki bölümün kapısını açıyor. Parametre tipi daraltıldığında işaretli kaynak
**derlenmiyor**: ortada bir yeniden tanımlama yoktur. Dönüş tipinde serbest olan daraltma,
parametrede yasaktır — ve bunun sonucu yalnız bir derleme hatası değildir.

## Sınırlayıcı Ölçüm: İmza Değişince Karar Taraf Değiştirir

`@Override` yazılmadığında aynı bildirim derlenir. O zaman ne olur? Çekirdeğe eklenen
`karsila` çifti tam olarak bu durumdadır: `Ust` bir `Ust` alır, `Alt` bir `Alt` alır, imzalar
denk düşmez, dolayısıyla ortada yeniden tanımlama değil **aşırı yükleme** vardır.

- **SN15** — İki çağrıda da nesne aynı nesnedir ve argüman aynı argümandır; değişen tek şey
  çağrının yapıldığı referansın bildirilen tipidir.

```java
// Imza.java — imza değişince yeniden tanımlama yerine aşırı yükleme oluyor
public class Imza {
    public static void main(String[] args) {
        Ust d = new Alt();
        Alt gercek = new Alt();
        System.out.println("Ust referansindan karsila : " + d.karsila(gercek));
        System.out.println("Alt referansindan karsila : " + ((Alt) d).karsila(gercek));
        System.out.println("Ust referansindan ad()    : " + d.ad());
        System.out.println("Alt referansindan ad()    : " + ((Alt) d).ad());
    }
}
```

```
Ust referansindan karsila : Ust.karsila(Ust)
Alt referansindan karsila : Alt.karsila(Alt)
Ust referansindan ad()    : alt yontem
Alt referansindan ad()    : alt yontem
```

Dört satır ikişer ikişer okunur. Alt iki satır yeniden tanımlamanın imzasıdır: iki ayrı
referans, **tek yanıt**. Referansın bildirilen tipini değiştirmek sonucu değiştirmiyor, çünkü
kararı veren taraf o değil.

Üst iki satır ise aşırı yüklemenin imzasıdır: aynı nesne, aynı argüman, **iki ayrı yanıt**.
Bir tek karakter bile değişmeden yazılabilecek iki çağrı, yalnız referansın bildirilen tipi
yüzünden ayrı gövdelere gidiyor. Karar tarafı yer değiştirmiştir.

Sınırın anlamı budur. Bir alt sınıfa yöntem yazan programcı çoğu zaman yeniden tanımladığını
sanır; imzada bir harf ya da bir parametre tipi kayarsa yeniden tanımlama hiç doğmaz ve
yerine, çalışma zamanı tipini hiç dinlemeyen bir seçim geçer. Program derlenir, çalışır ve
üst sınıfın sürümünü çağırır. Bu yüzden `@Override`, kaynağa eklenen bir açıklama değil,
kararın hangi taraftan geleceğini derleme zamanında dondurmanın tek yoludur.

## Özet

- Tek referans üzerinde sekiz karar okunur ve yanıt yarı yarıya bölünür: **dördü** çalışma
  zamanı tipinden, **dördü** bildirilen tipten gelir.
- Ayrım kuralı tektir: **çağrılan davranış** çalışma zamanı tipine, **seçilen ad** bildirilen
  tipe bakar. Arayüzden gelen varsayılan yöntem de yeniden tanımlanabilir ve ilk gruba girer.
- Aşağı dönüşüm nesneyi değiştirmez, bildirilen tipi değiştirir: `((Alt) d).etiket` **alt
  alan** verirken nesne baştan beri aynı nesnedir.
- Alanların aynı adla yeniden bildirilmesi **gizlemedir**: tek nesne `etiket` sorusuna iki
  yanıt verir ve birine yazmak ötekini değiştirmez — gizlenen alan yok olmaz.
- `@Override` bir davranış eklemez, bir varsayımı bildirir: adı yanlış yazılmış yöntem
  işaretsiz **derlenir**, işaretli **derlenmez**. Dönüş tipi daraltılabilir, genişletilemez.
- İmza denk düşmediğinde yeniden tanımlama doğmaz, aşırı yükleme olur ve karar taraf
  değiştirir: `karsila` iki referanstan iki ayrı yanıt verirken `ad()` tek yanıt verir.

## Sonraki Adım

Bu derste `Ust` bir gövde taşıyordu: her yöntemin bir gerçekleştirimi vardı ve alt sınıf
onları isterse yeniden tanımlıyordu. Arayüzden gelen varsayılan yöntem de tabloya bir satır
olarak girdi, ama arayüzün ne işe yaradığı sorulmadı. Sıradaki ders soyutlamanın iki aracını
yan yana koyar: bir yetenek soyut sınıfla da arayüzle de kurulabilir, biri tek üstle
sınırlıdır, öteki çoklu. Ölçüm iki arayüzün aynı varsayılan yöntemi verdiği duruma bakar —
derleyici kaç durumda karar vermeyi reddediyor, kaç durumda sessizce bir taraf kazanıyor — ve
durum taşıyan bir yeteneğin arayüzle neden kurulamadığını gösterir.
