---
title: 'Kalıtım ve Soyut Sınıflar'
source: 'https://academia.sh/tr/kurslar/php-nesne-modeli/kalitim-ve-soyut-siniflar'
course: 'Nesneye Dayalı PHP ve Standartlar'
language: tr
updated: '2026-10-21T05:03:34+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Soyut bir sınıfın örneklenemediğini ve final yöntemin yeniden tanımlanamadığını gösterir'
    - 'Üst sınıfın iç değişikliğinin alt sınıfın sayacını örtük biçimde değiştirdiğini ölçer'
    - 'Aynı sayacı bileşimle kurarak değişikliğin yerinde kaldığını doğrular'
---

# Kalıtım ve Soyut Sınıflar

Bir üst sınıfın içi değiştiğinde alt sınıfın sonucunun örtük biçimde kayması, soyut yöntem eklemenin alt sınıfları durdurması ve sayacı bileşimle kurmanın aynı değişikliği yerinde bırakması.

Sınıflar ve Nesneler dersinde bir sınıfın içindeki ad değişti ve kullanan etkilenmedi, çünkü araya görünürlük girmişti. Kalıtımda araya hiçbir şey girmez: alt sınıf, üst sınıfın yöntemlerini ve korunan özelliklerini doğrudan çağırır. Bu yüzden soru değişir. Üst sınıfın içi değiştiğinde alt sınıf ne yaşıyor?

## Alt Sınıf Kurmak

Kalıtım (inheritance), bir sınıfın başka bir sınıfın özelliklerini ve yöntemlerini devralmasıdır. Devralan sınıf alt sınıf (subclass), devralınan üst sınıf (parent class) olur. PHP'de `extends` sözcüğü bunu bildirir. `protected` görünürlüğü, bir üyeyi sınıfın kendisine ve alt sınıflara açar, dışarıya kapalı tutar. Alt sınıf bir yöntemi aynı adla yazarsa yöntemi yeniden tanımlar (overriding); üst sınıfın sürümüne yine de `parent::` ile ulaşılır.

```php
<?php
// Liste.php — protected bir ozellik ve iki yontem tasiyan ust sinif
class Liste
{
    protected array $ogeler = [];

    public function ekle(string $oge): void
    {
        $this->ogeler[] = $oge;
    }

    public function hepsiniEkle(array $ogeler): void
    {
        foreach ($ogeler as $oge) {
            $this->ekle($oge);
        }
    }

    public function say(): int
    {
        return count($this->ogeler);
    }
}
```

```php
<?php
// SayanListe.php — ekle() yontemini yeniden tanimlayan alt sinif
class SayanListe extends Liste
{
    public int $sayi = 0;

    public function ekle(string $oge): void
    {
        $this->sayi++;
        parent::ekle($oge);
    }
}
```

```php
<?php
// liste_kullan.php — alt sinif ust sinifin yontemini kullanir; protected ozellik disaridan kapali
require 'Liste.php';
require 'SayanListe.php';

$liste = new SayanListe();
$liste->hepsiniEkle(['a', 'b', 'c']);
echo $liste->sayi, ' ', $liste->say(), "\n";

try {
    echo count($liste->ogeler);
} catch (Error $hata) {
    echo get_class($hata), "\n";
}
```

```
3 3
Error
```

**NM5.** `hepsiniEkle()` alt sınıfta yazılmadı, devralındı; her öğe için `ekle()` çağrıldı ve `SayanListe::ekle()` her çağrıda sayacı artırdı: `3 3`. `protected` özellik alt sınıfa açıktı ama dışarıya kapalı kaldı. Burada dikkat çeken şey, alt sınıfın sayacının üst sınıfın bir iç ayrıntısına dayanmasıdır: `hepsiniEkle()`'nin tek tek `ekle()` çağırması.

## Soyut Sınıf ve final

Soyut sınıf (abstract class), örneği kurulamayan ve gövdesiz yöntem taşıyabilen sınıftır; alt sınıflar o yöntemleri yazmak zorundadır. Aşağıdaki sınıf bir yöntemi gövdesiz bildirir ve bir yöntemi gövdesiyle verir.

```php
<?php
// Rapor.php — govdesiz bir yontem ve ona dayanan govdeli bir yontem
abstract class Rapor
{
    abstract public function baslik(): string;

    public function yaz(): string
    {
        return strtoupper($this->baslik());
    }
}
```

```php
<?php
// Gunluk.php — soyut yontemi yazan somut alt sinif
class Gunluk extends Rapor
{
    public function baslik(): string
    {
        return 'gunluk';
    }
}
```

```php
<?php
// rapor_kullan.php — somut alt sinif kurulur, soyut sinif kurulamaz
require 'Rapor.php';
require 'Gunluk.php';

echo (new Gunluk())->yaz(), "\n";

try {
    new Rapor();
} catch (Error $hata) {
    echo get_class($hata), "\n";
}
```

```
GUNLUK
Error
```

Bir yöntem `final` ile işaretlenirse alt sınıflar onu yeniden tanımlayamaz. Dosya yüklenir yüklenmez durur. Aşağıdaki betik bir `final` yöntemi yeniden tanımlayan iki sınıfı bir dosyaya yazar ve çıkış kodunu basar.

```bash
# final.sh — final yontemi yeniden tanimlayan sinif yuklenemez
cat > Sabit.php <<'PHP'
<?php
class Taban
{
    final public function kimlik(): string { return 'taban'; }
}
class Alt extends Taban
{
    public function kimlik(): string { return 'alt'; }
}
PHP
php -d display_errors=0 Sabit.php; echo "cikis=$?"
```

```
cikis=255
```

**NM6.** `new Gunluk()` çalıştı ve `Rapor::yaz()` alt sınıfın `baslik()` yöntemini kullandı. `new Rapor()` ve `final` yöntemi yeniden tanımlayan dosya ise sinyal verdi: ilki `Error` fırlattı, ikincisi çıkış 255 ile durdu. Soyut sınıf ve `final`, kalıtımın iki ucudur: biri alt sınıfa yazma zorunluluğu getirir, öteki yazma yetkisini alır.

## Üst Sınıfın Yapıcısı

Alt sınıf kendi yapıcısını yazarsa üst sınıfın yapıcısı kendiliğinden çalışmaz. Üst sınıfın kurduğu durumun hazır olması için `parent::__construct()` açıkça çağrılır. Çağrı unutulursa üst sınıfın özellikleri kurulmadan kalır.

```php
<?php
// Kisi.php — ust sinif yapicisi, cagrilan ve unutulan alt sinif
class Kisi
{
    public function __construct(protected string $ad) {}

    public function selam(): string
    {
        return 'merhaba ' . $this->ad;
    }
}

class Calisan extends Kisi
{
    public function __construct(string $ad, private int $no)
    {
        parent::__construct($ad);
    }

    public function kart(): string
    {
        return $this->selam() . ' #' . $this->no;
    }
}

class Unutan extends Kisi
{
    public function __construct(private int $no) {}

    public function oku(): string
    {
        return $this->ad;
    }
}

echo (new Calisan('ali', 7))->kart(), "\n";

try {
    echo (new Unutan(1))->oku();
} catch (Error $e) {
    echo get_class($e), "\n";
}
```

```
merhaba ali #7
Error
```

`Calisan` üst sınıfın yapıcısını çağırdığı için `ad` kuruldu. `Unutan` çağırmadığı için `ad` hiç yazılmadı ve okunduğunda `Error` doğdu. Kalıtımda yapıcı zinciri yalnız yazılırsa işler.

## Yeniden Tanımlamada İmza ve final Sınıf

Alt sınıf bir yöntemi yeniden tanımlarken üst sınıfın imzasıyla uyumlu kalmak zorundadır; üst sınıfı tipli bir değişkende tutan kod alt sınıfla da aynı çağrıyı yapabilmelidir. Zorunlu bir parametre eklemek bu uyumu bozar. Bir sınıfın hiç genişletilmemesi isteniyorsa sınıf `final` ile işaretlenir; genişleten dosya yüklenir yüklenmez durur.

```bash
# imza.sh — zorunlu parametre eklemek ve final sinifi genisletmek bildirilemez yapar
cat > Imza.php <<'PHP'
<?php
class Taban { public function ekle(string $oge): void {} }
class Alt extends Taban { public function ekle(string $oge, int $adet): void {} }
PHP
cat > KapaliSinif.php <<'PHP'
<?php
final class Kapali {}
class Acan extends Kapali {}
PHP
php -d display_errors=0 Imza.php; echo "imza cikis=$?"
php -d display_errors=0 KapaliSinif.php; echo "final cikis=$?"
```

```
imza cikis=255
final cikis=255
```

İki durumda da hata yürütme sırasında değil, sınıf bildirimi sırasında çıktı. `final` yöntem tek bir davranışı, `final` sınıf bütün davranışı alt sınıfların elinden alır.

## Kalıtım Ne Zaman Yerindedir

Kalıtım iki sınıf arasında "bir tür" ilişkisi varsa yerindedir: `Calisan` bir `Kisi`'dir, `Gunluk` bir `Rapor`'dur. Yalnız kodu paylaşmak için kurulan kalıtım, alt sınıfı üst sınıfın bütün yöntemlerine ve gelecekteki bütün değişikliklerine bağlar. Bu ders boyunca ölçülen şey de budur: üst sınıfın içi değişince bağlı alt sınıflar bundan etkilenir. Paylaşılan kod bir ilişkiyi değil bir yeteneği anlatıyorsa, arayüz ya da trait daha az bağ kurar; bunlar sıradaki iki derstir.

## Ölçüm Çekirdeği

Bu dersin ölçümü `yayilim.php` dosyasını kullanır; dosya Sınıflar ve Nesneler dersinde satır satır açıklanan çekirdektir ve her dersin çalışma dizininde yeniden yazılır.

```php
<?php
// yayilim.php — bir degisikligin nereye ulastigini olcen yardimcilar

function yaz(string $dizin, array $dosyalar): void
{
    foreach ($dosyalar as $ad => $icerik) {
        if ($icerik === null) {
            continue; // null: dosya silindi
        }
        $yol = "$dizin/$ad";
        if (!is_dir(dirname($yol))) {
            mkdir(dirname($yol), 0777, true);
        }
        file_put_contents($yol, $icerik);
    }
}

function oku(string $dizin): array
{
    $dosyalar = [];
    $gezgin = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dizin, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($gezgin as $dosya) {
        $ad = substr($dosya->getPathname(), strlen($dizin) + 1);
        $dosyalar[$ad] = file_get_contents($dosya->getPathname());
    }
    return $dosyalar;
}

function kostur(string $dizin): array
{
    $komut = 'cd ' . escapeshellarg($dizin) . ' && php -d display_errors=0 main.php 2>/dev/null';
    exec($komut, $satirlar, $kod);
    return [$kod, implode("\n", $satirlar)];
}

function olc(string $ad, array $taban, array $degisiklik, array $baglanti = []): string
{
    if (array_key_exists('main.php', $degisiklik)) {
        throw new LogicException("$ad: kullanan duzenlenmez");
    }
    yaz("olcum/$ad/once", $taban);
    yaz("olcum/$ad/sonra", array_merge($taban, $degisiklik));
    [$kodOnce, $ciktiOnce] = kostur("olcum/$ad/once");
    [$kodSonra, $ciktiSonra] = kostur("olcum/$ad/sonra");
    if ($kodOnce !== 0) {
        throw new LogicException("$ad: taban calismadi");
    }
    $sinif = match (true) {
        $kodSonra !== 0 => 'kiran',
        $ciktiOnce !== $ciktiSonra => 'ortuk',
        array_intersect(array_keys($degisiklik), $baglanti) !== [] => 'baglantida',
        default => 'yerinde',
    };
    printf(
        "%-18s %d dosya  cikis=%-3d cikti %-6s -> %s\n",
        $ad,
        count($degisiklik),
        $kodSonra,
        $ciktiOnce === $ciktiSonra ? 'ayni' : 'farkli',
        $sinif
    );
    return $sinif;
}
```

## Üst Sınıfın İçi Değişir

İlk gözlem yukarıdaki `Liste` ve `SayanListe` ikilisini kullanır. Üst sınıfın `hepsiniEkle()` yöntemi artık tek tek `ekle()` çağırmaz, diziyi `array_merge()` ile bir seferde birleştirir. Alt sınıf ve kullanan `main.php` aynı kalır. Üst sınıfın dışarıya verdiği sözleşme de değişmez: iki yöntem aynı adı ve imzayı taşır.

```php
<?php
// kalitim.php — ust sinif toplu eklemede tekli ekleme yontemini artik cagirmaz
require_once 'yayilim.php';

olc('ust-sinif', [
    'liste.php' => <<<'PHP'
        <?php
        class Liste
        {
            protected array $ogeler = [];
            public function ekle(string $oge): void { $this->ogeler[] = $oge; }
            public function hepsiniEkle(array $ogeler): void
            {
                foreach ($ogeler as $oge) {
                    $this->ekle($oge);
                }
            }
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'liste.php';
        class SayanListe extends Liste
        {
            public int $sayi = 0;
            public function ekle(string $oge): void { $this->sayi++; parent::ekle($oge); }
        }
        $liste = new SayanListe();
        $liste->hepsiniEkle(['a', 'b', 'c']);
        echo $liste->sayi, "\n";
        PHP,
], [
    'liste.php' => <<<'PHP'
        <?php
        class Liste
        {
            protected array $ogeler = [];
            public function ekle(string $oge): void { $this->ogeler[] = $oge; }
            public function hepsiniEkle(array $ogeler): void
            {
                $this->ogeler = array_merge($this->ogeler, $ogeler);
            }
        }
        PHP,
]);

foreach (['once', 'sonra'] as $asama) {
    [$kod, $cikti] = kostur("olcum/ust-sinif/$asama");
    echo $asama, ': ', $cikti, "\n";
}
```

```
ust-sinif          1 dosya  cikis=0   cikti farkli -> ortuk
once: 3
sonra: 0
```

**NM7.** Çıkış kodu 0, ama çıktı değişti: sayaç `3` iken `0` oldu. Alt sınıf yalnız `ekle()` yöntemini yeniden tanımlamıştı; üst sınıf artık `ekle()` çağırmadığı için alt sınıfın yöntemi hiç çalışmadı. Kullanan kodda hata yok, uyarı yok, yalnızca yanlış bir sayı var. Bu, kırılgan üst sınıf sorunudur (fragile base class problem): alt sınıf, üst sınıfın sözleşmesine değil iç işleyişine güvenmişti ve iç işleyiş değişti. Bu dersin ölçü sınıfı örtüktür. M08/K02 çağrılan yöntemi hangi tipin seçtiğini ölçmüştü; burada ölçülen, üst sınıftaki değişikliğin alt sınıfa ulaşıp ulaşmadığıdır ve ulaştı.

## Soyut Yöntem Eklemek

Karşıt ölçüm değişikliği üst sınıfın içinden sözleşmesine taşır. Soyut bir sınıfa yeni bir `abstract` yöntem eklenir. Daha önce yazılmış `Gunluk`'un benzeri bir alt sınıf bu yöntemi bilmez.

```php
<?php
// soyut.php — soyut ust sinifa yeni bir abstract yontem eklenir
require_once 'yayilim.php';

olc('soyut-yontem', [
    'rapor.php' => <<<'PHP'
        <?php
        abstract class Rapor
        {
            abstract public function baslik(): string;
            public function yaz(): string { return strtoupper($this->baslik()); }
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'rapor.php';
        class Gunluk extends Rapor
        {
            public function baslik(): string { return 'gunluk'; }
        }
        echo (new Gunluk())->yaz(), "\n";
        PHP,
], [
    'rapor.php' => <<<'PHP'
        <?php
        abstract class Rapor
        {
            abstract public function baslik(): string;
            abstract public function alt(): string;
            public function yaz(): string { return strtoupper($this->baslik()); }
        }
        PHP,
]);
```

```
soyut-yontem       1 dosya  cikis=255 cikti farkli -> kiran
```

**NM8.** Aynı kullanan, aynı alt sınıf, aynı tek dosyalık değişiklik; bu kez kullanan durdu. Soyut yöntem bir alt sınıfa "yazmak zorundasın" der ve alt sınıf yazmadığında sınıf bildirilemez. Çıkış kodu 0 değil, sınıf kıran. Sözleşme gövdesiz olduğu için dil değişikliği yakalar; üst sınıfın iç değişikliğini yakalayacak bir sözleşme yoktu.

## Sınırlayıcı Ölçüm

Örtük sonucun nedeni kalıtımın kendisi mi, yoksa sayacın üst sınıfın içine bağlanması mı? Aynı sayaç bileşimle (composition) kurulduğunda ölçüm bunu ayırır. Bileşimde sınıf başka bir sınıfı genişletmez, onun bir örneğini taşır ve kendi iki yönteminde sayar. Üst sınıfta yapılan değişiklik ise öncekiyle aynıdır: `hepsiniEkle()` artık `ekle()` çağırmaz.

```php
<?php
// bilesim.php — sayac kalitimla degil bilesimle kurulur; ayni ust sinif degisikligi
require_once 'yayilim.php';

olc('bilesim', [
    'liste.php' => <<<'PHP'
        <?php
        class Liste
        {
            protected array $ogeler = [];
            public function ekle(string $oge): void { $this->ogeler[] = $oge; }
            public function hepsiniEkle(array $ogeler): void
            {
                foreach ($ogeler as $oge) {
                    $this->ekle($oge);
                }
            }
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'liste.php';
        class SayanListe
        {
            public int $sayi = 0;
            private Liste $ic;
            public function __construct() { $this->ic = new Liste(); }
            public function ekle(string $oge): void { $this->sayi++; $this->ic->ekle($oge); }
            public function hepsiniEkle(array $ogeler): void { $this->sayi += count($ogeler); $this->ic->hepsiniEkle($ogeler); }
        }
        $liste = new SayanListe();
        $liste->hepsiniEkle(['a', 'b', 'c']);
        echo $liste->sayi, "\n";
        PHP,
], [
    'liste.php' => <<<'PHP'
        <?php
        class Liste
        {
            protected array $ogeler = [];
            public function ekle(string $oge): void { $this->ogeler[] = $oge; }
            public function hepsiniEkle(array $ogeler): void
            {
                $this->ogeler = array_merge($this->ogeler, $ogeler);
            }
        }
        PHP,
]);

foreach (['once', 'sonra'] as $asama) {
    [$kod, $cikti] = kostur("olcum/bilesim/$asama");
    echo $asama, ': ', $cikti, "\n";
}
```

```
bilesim            1 dosya  cikis=0   cikti ayni   -> yerinde
once: 3
sonra: 3
```

**NM9.** Üst sınıfın değişikliği aynı, kullanan aynı; sonuç `3` kaldı. Bileşimde sayaç, üst sınıfın hangi yöntemi hangisine çağırdığına bakmaz: kendi iki yönteminde sayar ve üst sınıfa yalnız sözleşmeyi, yani iki yöntemin adını ve imzasını kullanarak ulaşır. Aynı iç değişiklik kalıtımda örtük, bileşimde yerinde kaldı. Ölçülen ayrım dilin değil tasarımın: kalıtım alt sınıfı üst sınıfın içine bağlar, bileşim bağlamaz. Bedel de buradan gelir: `SayanListe` artık bir `Liste` değildir.

## Özet

- `extends` bir alt sınıf kurar, `protected` üyeyi alt sınıfa açıp dışarıya kapalı tutar, `parent::` üst sınıfın yöntemine ulaşır.
- Soyut sınıf örneklenemez ve alt sınıfa gövdesiz yöntemleri yazma zorunluluğu getirir; `final` yöntem yeniden tanımlanamaz.
- Üst sınıf toplu eklemede `ekle()` çağırmayı bırakınca alt sınıfın sayacı `3`'ten `0`'a düştü; bu dersin ölçü sınıfı örtüktür.
- Soyut sınıfa `abstract` yöntem eklemek aynı kullananı durdurdu, kıran sınıf; sözleşme gövdesiz olduğu için dil yakalar.
- Sayaç bileşimle kurulunca aynı üst sınıf değişikliği yerinde kaldı: `3`.

## Sonraki Adım

Soyut yöntem gövdesiz bir sözleşme getirdi ve sözleşmeye yöntem eklemek alt sınıfı durdurdu. Arayüzler dersi bu sözleşmeyi kalıtımdan ayırıyor: bir arayüze yöntem eklenince onu gerçekleştiren sınıflar ne yaşıyor?
