---
title: 'Kalıtım ve `super`'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/kalitim-ve-super'
course: 'Nesne Yönelimi ve Metaprogramlama'
language: tr
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Boş bir alt sınıfın yöntemini atadan aldığını superclass, ancestors ve instance_methods ile ölçer'
    - 'Yeniden tanımlamada super çağrısının üst sınıftaki tanıma indiğini super_method ile gösterir'
    - 'Çıplak super ile super(...) arasındaki argüman farkını ArgumentError üzerinden ayırır'
---

# Kalıtım ve `super`

Boş bir alt sınıfın yöntemi üst sınıftan alması; superclass ve ancestors ile ata zinciri; yeniden tanımlamada super; initialize içinde super; çıplak super ile super(...) farkı.

Önceki ders `Hesap` sınıfında `bakiye_metni` yazılı olduğunu, ama `Vadeli` adlı boş bir alt sınıfın da aynı yöntemi çağırıp yanıt alabildiğini söyledi. Alt sınıfta tek satır tanım yok. Yöntemi oraya kim getirdi ve aynı yöntemi alt sınıfta yeniden yazmak neyi değiştirir?

Bu dersin yazanı ata yazanıdır: tanım alıcıdan uzakta, zincirde bir üst sınıfta durur. Ders önce boş bir alt sınıfı ölçer, sonra `super` ile yeniden tanımlamanın tanımı nasıl el değiştirdiğine bakar.

## Boş Bir Alt Sınıf Yöntemi Nereden Alır

`class Vadeli < Hesap` satırı `Hesap`'ı `Vadeli`'nin üst sınıfı yapar. Üst sınıfın yöntemleri alt sınıfın nesnelerine açıktır. Bu ilişkiye kalıtım denir (M08/K02). Aşağıdaki dosya `Vadeli` için çekirdekteki `ust sinif` gözlemini yeniden kurar. `yazan.rb` dosyası ilk derste verilen kuraldır ve aynen yüklenir.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

`yazan.rb` bu derste yeni bir şey eklemez; kaynak olarak durur. Ölçüm onu çağıran dosyadadır.

```ruby
# ust_sinif.rb — bos alt sinif yontemi atadan alir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

vadeli = Vadeli.new("can", 50)
p vadeli.bakiye_metni
p yazan(vadeli, :bakiye_metni)
p Vadeli.instance_methods(false)
```

```
"can 50"
["ata", "Hesap"]
[]
```

`vadeli.bakiye_metni` `can 50` verdi. `yazan` ata yazanını, kanıt olarak `Hesap`'ı buldu: tanımın sahibi alıcının sınıfı olan `Vadeli` değil, `Hesap`'tır. `Vadeli.instance_methods(false)` boş bir dizi verdi, yani `Vadeli` kendi başına hiçbir yöntem taşımıyor. `Vadeli.new("can", 50)` çağrısı da çalıştı, çünkü `initialize` de aynı yoldan `Hesap`'tan geldi. Alt sınıf boş olsa da nesne eksiksiz bir `Hesap` davranışı taşır.

**SM4.** Bu derste "üst sınıf", `class Vadeli < Hesap` satırında `<` işaretinin sağında yazılan sınıftır; "alt sınıf" solda yazılandır. "Ata", bir sınıfın zincirinde kendisinden sonra gelen her sınıf ya da modüldür. Kural, yöntemin sahibi alıcının sınıfı değilse yazanı ata sayar.

## Ata Zinciri

Üst sınıf tek bir sınıfı gösterir. Ama `Hesap`'ın da bir üst sınıfı vardır ve zincir oradan devam eder. Aşağıdaki dosya zinciri iki yoldan sorar: `superclass` bir adım, `ancestors` bütün sırayı verir.

```ruby
# zincir.rb — superclass ve ancestors
module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir
end

class Vadeli < Hesap
end

p Vadeli.superclass
p Hesap.superclass
p Vadeli.ancestors.take(5)
p Integer.ancestors.take(4)
```

```
Hesap
Object
[Vadeli, Hesap, Raporlanir, Object, Kernel]
[Integer, Numeric, Comparable, Object]
```

`Vadeli.superclass` `Hesap`, `Hesap.superclass` `Object` verdi. `ancestors` listesi sınıfları ve modülleri bir arada taşır: alıcının sınıfı en başta, sonra `Hesap`, sonra `Hesap`'ın karıştırdığı `Raporlanir` modülü, sonra `Object` ve `Kernel`. Modüllerin zincirdeki yeri bir sonraki dersin konusudur; burada yalnız `Raporlanir`'in `Hesap` ile `Object` arasında durduğuna dikkat etmek yeter. Çekirdek sınıflar aynı biçimde zincire sahiptir: `Integer` önce `Numeric`'e, sonra `Comparable` modülüne, sonra `Object`'e bağlanır. Yani `1.between?(0, 2)` çağrısındaki `between?` de `Integer`'ın değil, zincirdeki bir atanın yazdığı bir yöntemdir.

## Yeniden Tanımlamada super

Alt sınıf üst sınıftaki bir yöntemi aynı adla yeniden yazabilir. Bu işleme yeniden tanımlama denir. Yeniden tanımlama üst sınıfın tanımını silmez; alıcının zincirinde alt sınıf önce geldiği için ileti önce ona ulaşır. `super` ise bu tanımdan, zincirde bir sonraki aynı adlı tanıma iner. Aşağıdaki dosya `Vadeli2` sınıfında `bakiye_metni`'ni yeniden yazar ve gövdenin yarısını `super`'e bırakır.

```ruby
# yeniden_tanim.rb — super zincirde bir sonraki tanima iner
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

v2 = Vadeli2.new("can", 50)
puts v2.bakiye_metni
p v2.method(:bakiye_metni).owner
p v2.method(:bakiye_metni).super_method.owner
p v2.method(:bakiye_metni).super_method.super_method
```

```
vadeli can 50
Vadeli2
Hesap
nil
```

`vadeli can 50` çıktısı iki parçanın birleşimidir: `vadeli ` ön ekini `Vadeli2` yazdı, `can 50` kısmını `super` çağrısı üzerinden `Hesap` yazdı. `method(:bakiye_metni).owner` `Vadeli2` verdi, `super_method.owner` `Hesap`. `super_method` zincirde bir sonraki tanımı yöntem nesnesi olarak verir. `Hesap`'tan sonra `bakiye_metni` tanımlayan bir ata kalmadığı için `super_method.super_method` `nil` döndü. Yani `super` çağrısı zincirde aşağı yürümeye devam eden bir atlamadır, ama yalnız aynı adlı bir tanım olduğu sürece.

## Sahibi ve Kopyayı Ayırmak

`Vadeli.instance_methods(false)` boş çıktı, ama `Vadeli` yine de `bakiye_metni`'ne yanıt veriyor. İki soru ayrı tutulmalı: yöntem bu sınıfa mı yazılı, yoksa bu sınıfın nesneleri tarafından mı çağrılabiliyor? `instance_methods(false)` yalnız birincisini sorar. `method_defined?` ikincisini, `instance_method(...).owner` ise tanımın gerçekte nerede durduğunu söyler. Aşağıdaki dosya üç soruyu aynı sınıfa, ardından yeniden tanımlayan `Vadeli2`'ye yöneltir.

```ruby
# sahip_sorusu.rb — yazili olmak ile cagrilabilir olmak ayri sorulardir
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

p Vadeli.method_defined?(:bakiye_metni)
p Vadeli.instance_methods(false)
p Vadeli.instance_method(:bakiye_metni).owner
p Vadeli2.instance_methods(false)
p Vadeli2.instance_method(:bakiye_metni).owner
p Hesap.instance_method(:bakiye_metni).owner
```

```
true
[]
Hesap
[:bakiye_metni]
Vadeli2
Hesap
```

`Vadeli.method_defined?(:bakiye_metni)` doğru verdi, `Vadeli.instance_methods(false)` ise boş kaldı; ikisi çelişmez. İlki zincirin tamamına bakar, ikincisi yalnız sınıfın kendi gövdesine. `instance_method(...).owner` `Vadeli` için `Hesap` verdi: `Vadeli`'nin nesneleri çağırdığında çalışan tanım `Hesap`'a yazılmıştır, `Vadeli`'de bir kopyası yoktur. `Vadeli2` için tablo değişir: `instance_methods(false)` artık `bakiye_metni`'ni taşır ve `owner` `Vadeli2`'dir. `Hesap`'ın tanımı yerinde durur ve hâlâ `Hesap` sahipli çıkar. Yeniden tanımlama bir üzerine yazma değil, zincirin önüne ikinci bir tanım koymaktır.

## initialize İçinde super

`initialize` da bir yöntemdir ve alt sınıfta yeniden tanımlanabilir. Alt sınıf kendi ek değişkenini kurarken üst sınıfın değişkenlerini kurmasını `super` ile ister. Aşağıdaki dosya iki alt sınıf tanımlar ve ikisinin `initialize` yöntemi aynı imzayı taşır, ama `super` farklı yazılır.

```ruby
# initialize_super.rb — super(...) ile cıplak super
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Faizli < Hesap
  def initialize(sahip, bakiye, oran)
    super(sahip, bakiye)
    @oran = oran
  end
end

class Hatali < Hesap
  def initialize(sahip, bakiye, oran)
    super
    @oran = oran
  end
end

p Faizli.new("ece", 100, 5).instance_variables
begin
  Hatali.new("ece", 100, 5)
rescue ArgumentError => e
  puts e.class
end
```

```
[:@sahip, :@bakiye, :@oran]
ArgumentError
```

`Faizli` iki argümanı `super(sahip, bakiye)` ile üst sınıfa iletti ve `instance_variables` üç değişkeni sırasıyla listeledi: üst sınıfın iki değişkeni, ardından alt sınıfın `@oran`'ı. `Hatali` sınıfında çıplak `super` yazıldı ve `ArgumentError` doğdu. Çıplak `super`, çağıran yöntemin aldığı argümanların hepsini aynen iletir; `Hatali#initialize` üç argüman aldığı için `Hesap#initialize`'a üç argüman gitti, oysa o ikisini bekler.

**SM5.** Bu derste `super(...)` parantezli çağrıyı, "çıplak `super`" ise parantezsiz ve argümansız yazılan çağrıyı anlatır. Parantezli çağrı üst sınıfa yalnız yazılan argümanları iletir; çıplak çağrı yöntemin kendi argümanlarını olduğu gibi iletir. Alt sınıfın imzası üst sınıfınkinden farklıysa çıplak `super` yanlış argümanlar iletebilir; bu durumda parantezli çağrı yazılır.

Parantezli ve parantezsiz biçimin farkı bir üçüncü yazımla tamamlanır: boş parantezli `super()`. Çıplak `super` yöntemin kendi argümanlarını iletir, `super()` ise hiç argüman iletmez. Fark ancak üst sınıftaki yöntem varsayılan değerli bir parametre taşıdığında görünür olur. Aşağıdaki dosya üç alt sınıfta üç biçimi yan yana koyar.

```ruby
# super_bicimleri.rb — cıplak super, super() ve super(arguman)
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def etiket(onek = "hesap") = "#{onek}: #{@sahip}"
end

class Ciplak < Hesap
  def etiket(onek = "ciplak") = super
end

class Bos < Hesap
  def etiket(onek = "bos") = super()
end

class Acik < Hesap
  def etiket(onek = "acik") = super("sabit")
end

puts Ciplak.new("can", 1).etiket
puts Bos.new("can", 1).etiket
puts Acik.new("can", 1).etiket
puts Ciplak.new("can", 1).etiket("elle")
```

```
ciplak: can
hesap: can
sabit: can
elle: can
```

Çıplak `super` alt sınıfın `onek` parametresini üst sınıfa taşıdı; üst sınıftaki varsayılan `hesap` hiç kullanılmadı. `super()` hiçbir şey iletmedi, bu yüzden üst sınıf kendi varsayılanına döndü. `super("sabit")` yazılan değeri iletti ve alt sınıfın parametresini görmezden geldi. Dördüncü satır çıplak `super`'in ilettiğinin parametrenin o anki değeri olduğunu gösterir: çağrıya `elle` verilince üst sınıfa `elle` gitti. Çıplak `super`'i yararlı kılan bu, tehlikeli kılan da aynı özelliğidir; alt sınıfın imzası üst sınıfınkinden ayrıldığı anda iletilen şey de ayrılır.

Bir `super` çağrısı zincirde aynı adlı tanım bulamazsa ne olur? Aşağıdaki ölçüm bunu sorar.

```ruby
# yetim_super.rb — atada tanimi olmayan super
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Yetim < Hesap
  def olmayan = super
end

begin
  Yetim.new("a", 1).olmayan
rescue NoMethodError => e
  puts e.class
end
```

```
NoMethodError
```

`super` zincirde `olmayan` adlı bir tanım bulamadı ve `NoMethodError` verdi. Yani `super` bir sihir değil, bir arama sorgusudur: zincirde bir sonraki aynı adlı tanımı arar, bulamazsa her iletinin başına gelen hata ona da gelir.

Zincir iki basamakla sınırlı değildir. Her alt sınıf kendi `super` çağrısıyla bir sonrakine iner ve çağrılar sırayla geri döner. Aşağıdaki dosya üç sınıflı bir zincir kurar ve `super_method` ile zinciri baştan sona yürüyüp sahiplerini toplar.

```ruby
# uc_basamak.rb — super_method zinciri sonuna kadar yurunur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

class Altin < Vadeli2
  def bakiye_metni = "altin #{super}"
end

a = Altin.new("can", 50)
puts a.bakiye_metni

sahipler = []
yontem = a.method(:bakiye_metni)
while yontem
  sahipler << yontem.owner
  yontem = yontem.super_method
end
p sahipler
```

```
altin vadeli can 50
[Altin, Vadeli2, Hesap]
```

Metin, en içteki tanımdan dışa doğru birleşti: `Hesap` `can 50` kısmını, `Vadeli2` `vadeli ` ekini, `Altin` `altin ` ekini yazdı. Alıcıya en yakın tanım ilk çalışan, ama gövdesinin ortasında bir sonrakini bekleyen tanımdır. `super_method` döngüsü sahipleri aynı sırayla verdi ve `nil` döndüğünde durdu. Böylece `super` çağrısının görünmez bir mekanizma olmadığı, `ancestors` sırasının üzerinden okunan bir yol olduğu doğrulanır.

## Zincir Çağrı Anında Aranır

`Vadeli` sınıfı tanımlandığında `Hesap`'ın tanımlarını kendine kopyalamadı; çağrı geldiğinde zincire bakar. Bunun gözlenebilir bir sonucu var: `Vadeli` kurulduktan sonra `Hesap`'a eklenen bir yöntem, `Vadeli` nesnesinde de hemen çağrılabilir. Aşağıdaki dosya aynı nesneye aynı soruyu, `Hesap` yeniden açılmadan önce ve sonra yöneltir.

```ruby
# gec_eklenen.rb — atanin sonradan kazandigi yontem alt sinifta gorunur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Vadeli < Hesap
end

v = Vadeli.new("can", 50)
p v.respond_to?(:faiz)

class Hesap
  def faiz = @bakiye / 10
end

p v.respond_to?(:faiz)
p v.faiz
p Vadeli.instance_methods(false)
```

```
false
true
5
[]
```

İlk `respond_to?` yanlış verdi, `Hesap` yeniden açıldıktan sonra doğru verdi ve `v.faiz` `5` döndü. `v` bu arada hiç yeniden kurulmadı. `Vadeli.instance_methods(false)` hâlâ boş: `faiz` alt sınıfa yazılmadı, yalnız zincirdeki bir atanın tanımı oldu. Aranan tanım çağrı anında zincirin o anki hâline göre bulunur.

**SM13.** Bu derste "zincir", alıcının sınıfından başlayıp `BasicObject`'e kadar giden arama sırasıdır. Zincirin üyeleri sınıfa kopyalanmaz; arama her çağrıda sıfırdan yapılır. Bir atanın sonradan kazandığı yöntem bu yüzden alt sınıfın mevcut nesnelerinde de görünür.

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

`Vadeli` hiçbir şey yazmadı ve yazanı ata çıktı. `Vadeli2` gövdenin yarısını atadan aldı, öbür yarısını kendisi yazdı. Kural bu ikinciyi nasıl sınıflandırır?

```ruby
# yarim_ata.rb — super cagiran yeniden tanim programci sayilir
require_relative "yazan"

class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

class Vadeli < Hesap
end

class Vadeli2 < Hesap
  def bakiye_metni = "vadeli #{super}"
end

p yazan(Vadeli.new("can", 50), :bakiye_metni)
p yazan(Vadeli2.new("can", 50), :bakiye_metni)
```

```
["ata", "Hesap"]
["programci", "Vadeli2, def govdesi"]
```

`Vadeli` için ata, `Vadeli2` için programcı çıktı. `Vadeli2`'nin gövdesi yarısını atadan alsa da kural bunu ölçmez. Kural zincirde ilk bulunan tanımı sayar ve o tanımın sahibini alıcının sınıfıyla karşılaştırır; zincirin geri kalanına bakmaz.

**SM6.** Bu derste "ilk bulunan tanım", alıcıdan başlayan arama sırasında karşılaşılan ilk tanımdır. `instance_method(ad)` onu verir. `super` çağrısının zincirin devamında başka bir tanıma indiğini kural görmez, çünkü ilk tanımın gövdesine bakmaz.

Sonuç iki yönlüdür. Alt sınıfa tek bir `def` yazmak, yazanı atadan programcıya çevirir; gövdenin ne kadarının üst sınıfa devredildiği değişmez. Öte yandan yazanı programcı olan bir yöntemin gövdesi tamamen o programcıya ait olmak zorunda değildir. Yazan kategorisi, gövdenin kaynağını değil, zincirdeki ilk tanımın yerini söyler.

## Özet

- Boş bir alt sınıf yöntemlerini üst sınıftan alır: `Vadeli.instance_methods(false)` boştur ve `Vadeli.new("can", 50).bakiye_metni` atadan gelir.
- `superclass` bir adımı, `ancestors` bütün zinciri verir; `Vadeli.ancestors.take(5)` alt sınıf, üst sınıf, karıştırılmış modül, `Object` ve `Kernel` sırasını gösterir.
- Yeniden tanımlama üst sınıfın tanımını silmez; `super` zincirde bir sonraki aynı adlı tanıma iner ve `super_method.owner` o tanımın sahibini verir.
- `initialize` içinde `super(sahip, bakiye)` yalnız yazılan argümanları iletir; aynı imzada çıplak `super` argümanları aynen ilettiği için `ArgumentError` verir.
- Bu dersin yazanı atadır: `ust sinif` satırında `bakiye_metni`'ni `Hesap` yazdı, `Vadeli` hiçbir şey yazmadı.
- Atada tanımı olmayan `super` `NoMethodError` verir; `super` çağıran yeniden tanımlamada yazan programcı çıkar, çünkü kural ilk bulunan tanımı sayar.

## Sonraki Adım

`Hesap` sınıfında `rapor` diye bir yöntem hiç yazılmadı, ama `hesap.rapor` çalıştı ve `rapor: ece 100` verdi. Yöntem ne bir üst sınıftan geliyor, ne de sınıfın kendi gövdesinden. Zincirde `Hesap` ile `Object` arasında duran `Raporlanir` o yöntemi nasıl taşıyor? Sonraki ders, Modüller ve Karışımlar, bunu ölçer.
