SEN1002 · 2. hafta · Inheritance

Kalıtım: extends, super ve constructor zinciri

Alt sınıf üst sınıftan ne alır, constructor'lar hangi sırayla çalışır, overriding ile overloading farkı. Çıktı sorularının favorisi.

inheritancesuperclasssubclassextendssuperconstructor chainingprotectedmethod overridingmethod overloadingObject classis-a relationshipfinal

Kalıtım (inheritance), var olan bir sınıfın üstüne yeni bir sınıf kurmaktır. Yeni sınıf (alt sınıf, subclass) eskisinin (üst sınıf, superclass) alanlarını ve metotlarını alır, üstüne kendi şeylerini ekler ya da bazı metotları değiştirir. Sınavda bu konu en çok iki biçimde gelir: constructor’ların hangi sırayla bastığını soran çıktı soruları ve super ile ilgili derleme hataları.

Altın kurallar:

  1. Her constructor önce üst sınıfın constructor’ını çağırır. Yazmazsan derleyici super(); ekler.
  2. Çıktı sırası tepeden aşağı: önce Object, sonra üst sınıf, en son alt sınıf.
  3. private alan miras alınır ama adıyla erişilemez. Alt sınıf getter kullanır ya da alan protected olur.
  4. Override: aynı imza, nesnenin gerçek tipine göre çalışır. Overload: farklı parametreler, derleme zamanında seçilir.
  5. Alanlar override edilmez. Sadece metotlar.

extends ve “is-a” ilişkisi

class Manager extends Employee yazınca “her Manager bir Employee’dir” demiş olursun (is-a relationship). Manager, Employee’nin bütün public ve protected üyelerini kendi üyesiymiş gibi kullanır. Java’da bir sınıf sadece bir sınıftan türeyebilir (single inheritance). Birden fazla tip davranışı istiyorsan arayüz (interface) kullanırsın (4. hafta).

Kalıtımı ne zaman kullanacağını “is-a” testiyle anlarsın. “Bir Manager bir Employee’dir” doğru, kalıtım uygun. “Bir Car bir Engine’dir” yanlış; arabanın bir motoru vardır (has-a). O durumda Car içine bir Engine alanı koyarsın (composition, 7. hafta).

class Employee {
    private String name;
    protected double salary;     // alt sınıflar doğrudan görebilir

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
        System.out.println("Employee ctor: " + name);
    }
    public String getName() { return name; }
    public double pay() { return salary; }
    @Override
    public String toString() { return "Employee " + name + " " + pay(); }
}

class Manager extends Employee {
    private double bonus;        // Manager'a özel

    public Manager(String name, double salary, double bonus) {
        super(name, salary);     // üst sınıfın constructor'ı
        this.bonus = bonus;
        System.out.println("Manager ctor: bonus " + bonus);
    }
    @Override
    public double pay() { return super.pay() + bonus; }
    @Override
    public String toString() { return "Manager " + getName() + " " + pay(); }
}
Manager m = new Manager("Selin", 5000, 1500);
System.out.println(m);
System.out.println(m.pay());
Employee ctor: Selin
Manager ctor: bonus 1500.0
Manager Selin 6500.0
6500.0

Manager’ın toString’inde name değil getName() yazdık. name Employee’de private; Manager içinde name yazsaydın name has private access in Employee derleme hatası alırdın.

Constructor zinciri (constructor chaining)

Constructor’lar miras alınmaz. Ama alt sınıf nesnesi yaratılırken üst sınıfın parçası da kurulmak zorunda. Bu yüzden her constructor, ilk satırında üst sınıfın bir constructor’ını çağırır:

  • super(argümanlar) yazarsan o constructor çağrılır.
  • Hiçbir şey yazmazsan derleyici gizlice super(); ekler (parametresiz).
  • Üst sınıfta parametresiz constructor yoksa bu gizli çağrı derleme hatası verir.
class A { A() { System.out.println("A"); } }
class B extends A { B() { System.out.println("B"); } }      // gizli super()
class C extends B { C() { super(); System.out.println("C"); } }
new C();
A
B
C
new C()
  C() çağrıldı → ilk iş super() → B()
      B() çağrıldı → ilk iş super() → A()
          A() çağrıldı → super() → Object() (sessiz)
          A gövdesi: "A"
      B gövdesi: "B"
  C gövdesi: "C"

TUZAK · this(...) ve super(...) birlikte. Bir constructor ya this(...) ya super(...) ile başlar, ikisi birden olmaz. this(...) ile başlayan constructor üst sınıfı kendisi çağırmaz; zincirin sonundaki constructor çağırır. Aşağıdaki Truck örneği bunu gösteriyor.

class Vehicle {
    int wheels;
    Vehicle(int w) { wheels = w; System.out.println("Vehicle " + w); }
    Vehicle()      { this(4);    System.out.println("Vehicle()"); }
}
class Bike  extends Vehicle { Bike()  { super(2); System.out.println("Bike"); } }
class Truck extends Vehicle { Truck() {           System.out.println("Truck"); } }

Bike b = new Bike();
Truck t = new Truck();
System.out.println(b.wheels + " " + t.wheels);
Vehicle 2
Bike
Vehicle 4
Vehicle()
Truck
2 4

Alan ilk değerleri ne zaman atanır?

Nesne yaratılırken sıra şudur: (1) bütün alanlar varsayılan değer alır (0, null, false), (2) super(...) çalışır, (3) bu sınıfın alan ilk değerleri (int x = 7;) atanır, (4) constructor gövdesinin kalanı çalışır. Üst sınıfın constructor’ı override edilmiş bir metodu çağırırsa, o metot alt sınıfın alanlarını henüz varsayılan değerinde görür:

class Base {
    Base() { System.out.println("Base ctor"); hello(); }
    void hello() { System.out.println("Base hello"); }
}
class Derived extends Base {
    int x = 7;
    Derived() { System.out.println("Derived ctor x=" + x); }
    @Override void hello() { System.out.println("Derived hello x=" + x); }
}
new Derived();
Base ctor
Derived hello x=0
Derived ctor x=7

TUZAK · super(...) ilk satır kuralı. Ders kitabı ve sınav, super(...) çağrısının constructor’ın ilk satırı olması gerektiğini esas alır; Java 24 ve öncesinde öyledir. Java 25’teki flexible constructor bodies özelliği, öncesine this’e dokunmayan satırlara izin verdi (--release 21 ile derlersen flexible constructors is not supported hatası alırsın). Sınavda “super ilk satırda değil” diye bir şık görürsen eski kurala göre cevapla: derleme hatası.

protected ve erişim

Üst sınıftaki üyeAlt sınıf içinden erişim
publicevet
protectedevet (alt sınıf başka pakette olsa bile)
belirleyicisiz (package-private)sadece aynı paketteyse
privatehayır (nesnede var, adıyla erişilemez)

protected kolaylık sağlar ama kapsüllemeyi biraz gevşetir. Çoğu durumda alanı private tutup getter vermek daha iyidir; sınavda iki çözüm de kabul görür.

Overriding ve overloading

İki kavram adları yüzünden karıştırılır ama tamamen farklı mekanizmalar:

Overriding (geçersiz kılma)Overloading (aşırı yükleme)
Nerede?Alt sınıfta, üst sınıftaki metodu yeniden yazarsınAynı sınıfta (ya da miras alınanla) aynı ad
Parametre listesiAynıFarklı (sayı, tip ya da sıra)
Dönüş tipiAynı ya da alt tipiÖnemli değil, tek başına ayırt etmez
ErişimDaralamaz (public → public)Serbest
Seçim ne zaman?Çalışma zamanında, nesnenin gerçek tipine göreDerleme zamanında, argüman tiplerine göre
@OverrideYazılırYazılmaz
class Printer {
    void show(int a)    { System.out.println("int " + a); }
    void show(double a) { System.out.println("double " + a); }
    void show(String s) { System.out.println("String " + s); }
}
Printer pr = new Printer();
pr.show(3);
pr.show(3.0);
pr.show('A');     // char → int'e genişler
pr.show(5L);      // long → double'a genişler
pr.show("3");
int 3
double 3.0
int 65
double 5.0
String 3

Override edilen metodun içinde super.metot() ile üst sınıfın sürümünü çağırabilirsin. Bu, kodu tekrar yazmadan üstüne eklemenin standart yolu:

class Shape  { String name() { return "shape"; } }
class Circle extends Shape {
    @Override String name() { return "circle<" + super.name() + ">"; }
}
System.out.println(new Circle().name());
circle<shape>

Alan gizleme (field hiding)

Alt sınıf üst sınıfla aynı adda bir alan tanımlarsa iki ayrı alan olur. Hangi alanın okunacağına değişkenin tipi karar verir, nesnenin değil. Metotlarda ise nesnenin gerçek tipi karar verir.

class P { String tag = "P"; String getTag() { return tag; } }
class Q extends P { String tag = "Q"; @Override String getTag() { return tag; } }
P obj = new Q();
System.out.println(obj.tag + " " + obj.getTag());
P Q

Object sınıfı

extends yazmadığın her sınıf otomatik olarak java.lang.Object’ten türer. Yani her nesne şu metotlara sahiptir:

MetotVarsayılan davranışGenelde ne yapılır?
toString()SinifAdi@hashOkunabilir metin için override
equals(Object o)this == oİçerik karşılaştırması için override
hashCode()Nesneye özgü sayıequals override edilirse bu da edilir
getClass()Çalışma zamanındaki sınıfgetClass().getSimpleName() gibi kullanılır
Book b1 = new Book("Dune"), b2 = new Book("Dune");   // equals override YOK
System.out.println(b1.equals(b2));
System.out.println(b1.getClass().getSimpleName());
System.out.println(b1 instanceof Object);
false
Book
true

final

  • final class: kimse ondan türeyemez (String, Integer böyle).
  • final metot: alt sınıf override edemez, ama overload serbest.
  • final alan: bir kez atanır, sonra değişmez.

Sık yapılan hatalar

Yanlış düşünceDoğrusu
Constructor’lar miras alınırAlınmaz; her sınıf kendi constructor’ını yazar, super(...) ile üsttekini çağırır
super yazmazsam üst sınıf kurulmazDerleyici gizli super() ekler; üstte parametresiz constructor yoksa hata
Çıktı alt sınıftan başlarGövdeler tepeden aşağı çalışır: A, B, C
Alt sınıf private alanı kullanabilirAdıyla erişemez; getter ya da protected
Aynı ad, farklı parametre = overrideBu overload; override için parametreler aynı olmalı
Override ederken public’i silebilirimErişim daraltılamaz
Alanlar da override edilirGizlenir; ref.alan değişkenin tipine bakar
Üst constructor’ın çağırdığı metot üst sınıfınkiOverride edilmişse alt sınıfınki çalışır, alanları henüz varsayılanda
super.super.metot()Böyle bir şey yok, sadece bir üst

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

What is printed by new C();?

class A { A() { System.out.println("A"); } }
class B extends A { B() { System.out.println("B"); } }
class C extends B { C() { super(); System.out.println("C"); } }
Cevabı göster
A, B, C (alt alta)

Her constructor'ın ilk işi üst sınıfın constructor'ını çağırmak. B'de super() yazılmasa da derleyici ekler. Çağrı C → B → A diye iner, ama gövdeler en tepeden başlayarak çalışır: önce A basılır, sonra B, en son C.

Soru 2orta

What is printed?

class Employee {
    private String name; protected double salary;
    Employee(String name, double salary) {
        this.name = name; this.salary = salary;
        System.out.println("Employee ctor: " + name);
    }
    String getName() { return name; }
    double pay() { return salary; }
}
class Manager extends Employee {
    private double bonus;
    Manager(String n, double s, double b) {
        super(n, s); bonus = b;
        System.out.println("Manager ctor: bonus " + bonus);
    }
    @Override double pay() { return super.pay() + bonus; }
}
// in main:
Manager m = new Manager("Selin", 5000, 1500);
System.out.println(m.pay());
Cevabı göster
Employee ctor: Selin / Manager ctor: bonus 1500.0 / 6500.0

super(n, s) önce Employee'nin constructor'ını çalıştırır: Employee ctor: Selin. Sonra Manager'ın gövdesi: bonus double olduğu için 1500.0. pay() override edilmiş: super.pay() 5000.0 döner, üstüne 1500 eklenir: 6500.0.

Soru 3orta

Find the compile error.

class Vehicle {
    int wheels;
    Vehicle(int w) { wheels = w; }
}
class Car extends Vehicle {
    Car() { System.out.println("Car"); }
}
Cevabı göster
Car() içinde gizli super() çağrısı var, Vehicle'da parametresiz constructor yok

super(...) yazmazsan derleyici super(); ekler. Vehicle sadece Vehicle(int) tanımladığı için parametresiz constructor yok. javac: constructor Vehicle in class Vehicle cannot be applied to given types; required: int. Çözüm: Car'ın ilk satırına super(4); yaz.

Soru 4kolay

Find the compile error.

class Employee {
    private double salary = 100;
}
class Manager extends Employee {
    double pay() { return salary * 2; }
}
Cevabı göster
salary private, alt sınıf bile doğrudan erişemez

Alt sınıf private alanı miras alır (nesnenin içinde vardır) ama adıyla erişemez. javac: salary has private access in Employee. Çözüm: alanı protected yap ya da Employee'ye getSalary() ekleyip onu çağır.

Soru 5zor

What is printed?

class Printer {
    void show(int a)    { System.out.println("int " + a); }
    void show(double a) { System.out.println("double " + a); }
    void show(String s) { System.out.println("String " + s); }
}
// in main:
Printer p = new Printer();
p.show('A');
p.show(5L);
Cevabı göster
int 65 ve double 5.0

Overloading'de hangi metodun çağrılacağını derleyici argüman tipine bakarak seçer. 'A' bir char, birebir eşleşme yok; char'ın genişleyebildiği en yakın tip int: int 65 (A'nın kodu). 5L long; long int'e daralamaz, double'a genişler: double 5.0.

Soru 6orta

What is printed?

class Animal {
    String sound() { return "..."; }
    String describe() { return "I say " + sound(); }
}
class Dog extends Animal {
    @Override String sound() { return "Woof"; }
}
// in main:
Animal d = new Dog();
System.out.println(d.describe());
Cevabı göster
I say Woof

describe() Animal'da yazılı ama içindeki sound() çağrısı o anki nesnenin, yani Dog'un sürümünü çalıştırır. Override edilmiş metot her zaman nesnenin gerçek sınıfından seçilir (3. haftada dynamic binding).

Soru 7orta

Find the compile error.

class Animal {
    public String sound() { return "..."; }
}
class Cat extends Animal {
    String sound() { return "Meow"; }
}
Cevabı göster
Override eden metot erişimi daraltamaz (public → package-private)

Override ederken erişim belirleyici aynı kalabilir ya da genişleyebilir, daralamaz. Animal'da public olan metot Cat'te belirleyicisiz (package-private) yazılmış. javac: attempting to assign weaker access privileges; was public. Çözüm: Cat'te de public yaz.

Soru 8zor

What is printed?

class Vehicle {
    int wheels;
    Vehicle(int w) { wheels = w; System.out.println("Vehicle " + w); }
    Vehicle() { this(4); System.out.println("Vehicle()"); }
}
class Bike extends Vehicle { Bike() { super(2); System.out.println("Bike"); } }
class Truck extends Vehicle { Truck() { System.out.println("Truck"); } }
// in main:
Bike b = new Bike();
Truck t = new Truck();
System.out.println(b.wheels + " " + t.wheels);
Cevabı göster
Vehicle 2 / Bike / Vehicle 4 / Vehicle() / Truck / 2 4

Bike açıkça super(2) çağırır: Vehicle 2, sonra Bike. Truck'ta super yazılmamış, gizli super() parametresiz Vehicle'ı çağırır; o da this(4) ile Vehicle(int)'e devreder: önce Vehicle 4, sonra Vehicle(), en son Truck. Tekerlekler 2 ve 4.

Soru 9zor

What is printed?

class Base {
    Base() { System.out.println("Base ctor"); hello(); }
    void hello() { System.out.println("Base hello"); }
}
class Derived extends Base {
    int x = 7;
    Derived() { System.out.println("Derived ctor x=" + x); }
    @Override void hello() { System.out.println("Derived hello x=" + x); }
}
// in main:
new Derived();
Cevabı göster
Base ctor / Derived hello x=0 / Derived ctor x=7

Nesne Derived olduğu için Base'in constructor'ı içindeki hello() çağrısı bile Derived'ın sürümünü çalıştırır. Ama o an Derived'ın alan ilk değerleri (x = 7) henüz atanmadı; bu atama super() bittikten sonra yapılır. x varsayılan değeri 0 ile basılır. Sonra x = 7 olur ve Derived'ın gövdesi çalışır.

Soru 10zor

What is printed?

class P { String tag = "P"; String getTag() { return tag; } }
class Q extends P { String tag = "Q"; @Override String getTag() { return tag; } }
// in main:
P obj = new Q();
System.out.println(obj.tag + " " + obj.getTag());
Cevabı göster
P Q

Alanlar override edilmez, gizlenir (field hiding). obj.tag derleme zamanındaki tipe (P) bakar: "P". Metotlar ise override edilir: getTag() Q'nun sürümü çalışır ve Q'nun tag'ini döner: "Q". Bu yüzden alt sınıfta üst sınıfla aynı adlı alan açma.

Soru 11kolay

Which statement about final is true? (a) A final method cannot be overloaded. (b) A final class cannot be extended. (c) A final field can be changed in a subclass.

  1. (a)
  2. (b)
  3. (c)
Cevabı göster
(b)

final class miras alınamaz: class Key extends Locked için javac cannot inherit from final Locked der (String de final bir sınıftır). final metot override edilemez ama overload edilebilir; (a) yanlış. final alan bir kez atanır, kimse değiştiremez; (c) yanlış.

Soru 12orta

Write a class Student that extends Person (which has a private name, a constructor Person(String name), getName() and toString() returning Person: <name>). Student adds a private double gpa, a constructor Student(String name, double gpa), and overrides toString() to return Person: Deniz, GPA 3.4 style text.

Cevabı göster
super(name) + super.toString() kullanan alt sınıf

name Person'da private, bu yüzden Student onu ancak super(name) ile doldurabilir. toString'de üst sınıfın metnini tekrar yazmak yerine super.toString() çağır:

public class Student extends Person {
    private double gpa;

    public Student(String name, double gpa) {
        super(name);          // ilk satır
        this.gpa = gpa;
    }
    public double getGpa() { return gpa; }

    @Override
    public String toString() {
        return super.toString() + ", GPA " + gpa;
    }
}
new Student("Deniz", 3.4) basılınca: Person: Deniz, GPA 3.4.

Doğrulama: Tüm kod örnekleri ve cevaplar JDK 26 ile çalıştırıldı (java verify/SEN1002/W02.java); derleme hataları javac ile denendi (verify/SEN1002/err/E02a-e.java, E02d ayrıca --release 21 ile).

Deftere not kartı

class Alt extends Ust → "Alt is-a Ust". Java'da tek kalıtım (bir extends). Alt sınıf: public/protected üyeleri kullanır, private'ı adıyla GÖREMEZ. Constructor miras alınmaz. Her ctor ilk iş üst ctor'u çağırır. super(...) yazmazsan derleyici super() ekler → üstte parametresiz ctor yoksa HATA. Çağrı aşağıdan yukarı, çalışma YUKARIDAN AŞAĞI: A, B, C. Alan ilk değerleri (int x = 7) super() bittikten SONRA atanır. super.metot() → üst sınıfın sürümü. super.super yok. Override: aynı ad + aynı parametreler; dönüş tipi aynı (ya da alt tip); erişim daralamaz. Overload: aynı ad, FARKLI parametre listesi; derleme zamanında seçilir. Alan override edilmez, gizlenir: ref.alan → değişkenin tipi; ref.metot() → nesnenin tipi. final class: extends yok. final metot: override yok. static metot: override yok (gizlenir). Her sınıfın tepesinde Object: toString, equals, hashCode, getClass.