Method Overriding
Overriding Nedir?
Method overriding, bir subclass'ın superclass'tan miras aldığı metodu yeniden tanımlamasıdır. Metot ismi, parametreleri ve dönüş tipi aynı kalır — sadece gövdesi (body) değişir.
Bunu şöyle düşün: Babanın bir yemek tarifi var — "makarna yap" diyor. Sen de makarna yapıyorsun ama kendi sosunu ekliyorsun. Tarif adı aynı, sonuç farklı. İşte overriding bu: aynı isimli metodu kendi ihtiyacına göre yeniden yazmak.
Temel Override Örneği
class Animal {
void makeSound() {
System.out.println("Some generic sound...");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Miyav!");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Hav hav!");
}
}Animal a = new Animal();
Cat c = new Cat();
Dog d = new Dog();
a.makeSound(); // Some generic sound...
c.makeSound(); // Miyav!
d.makeSound(); // Hav hav!Cat ve Dog sınıfları makeSound() metodunu kendi versiyonlarıyla değiştirdi. Artık bu nesnelerde superclass'ın versiyonu değil, kendi versiyonları çalışır.
@Override Annotation
@Override annotation'ı zorunlu değil ama mutlaka kullanmalısın. Neden? Çünkü compiler'a "ben burada bir metodu override ediyorum" demiş oluyorsun. Eğer yanlışlıkla metot ismini yazım hatası yaparsan, compiler seni uyarır.
class Animal {
void makeSound() {
System.out.println("...");
}
}
class Cat extends Animal {
// @Override olmadan — derleme hatası vermez ama yeni metot tanımlar!
void makeS0und() { // Dikkat: 'o' yerine '0' yazmışız
System.out.println("Miyav!");
}
}Yukarıdaki kodda makeS0und diye yeni bir metot tanımladık. Override olmadı! Ama @Override koysaydık:
@Override
void makeS0und() { // DERLEME HATASI! Superclass'ta böyle metot yok
System.out.println("Miyav!");
}Compiler "Bu metot hiçbir şeyi override etmiyor" diye hata verir. Böylece hatayı derleme zamanında yakalarsın.
💡 İpucu:
@Overrideher zaman koy. Bu sadece iyi bir alışkanlık değil, hata yakalama mekanizması. IDE'ler de otomatik olarak ekler.
Overriding Kuralları
Override'ın çalışması için belli kurallar var. Bunları bilmezsen derleme hataları alırsın.
1. Metot imzası aynı olmalı:
İsim ve parametre listesi birebir aynı olmalı.
class Parent {
void greet(String name) { }
}
class Child extends Parent {
@Override
void greet(String name) { } // ✅ Doğru override
// void greet(int id) { } // ❌ Bu override değil, overload!
// void greet() { } // ❌ Bu da override değil, overload!
}2. Dönüş tipi aynı veya covariant olmalı:
Dönüş tipi aynı olabilir ya da alt tipini (subtype) döndürebilirsin. Buna covariant return type denir.
class Animal {
Animal create() {
return new Animal();
}
}
class Dog extends Animal {
@Override
Dog create() { // Dog, Animal'ın alt tipi — geçerli!
return new Dog();
}
}Ama primitive tipler için covariant yok. int döndüren bir metodu long döndürecek şekilde override edemezsin.
3. Erişim belirteci aynı veya daha geniş olmalı:
Override eden metot, override edilen metottan daha kısıtlayıcı olamaz.
class Parent {
protected void doSomething() { }
}
class Child extends Parent {
@Override
public void doSomething() { } // ✅ public > protected, OK
// @Override
// private void doSomething() { } // ❌ private < protected, HATA!
}Sıralama: public > protected > default > private. Override ederken eşit ya da sağa (daha açık) gidebilirsin, sola (daha kapalı) gidemezsin.
4. Exception kuralı:
Override eden metot, superclass metodundan daha geniş checked exception fırlatamaz. Daha dar fırlatabilir veya hiç fırlatmayabilir.
class Parent {
void read() throws IOException { }
}
class Child extends Parent {
@Override
void read() throws FileNotFoundException { } // ✅ Daha dar (alt tip)
// @Override
// void read() throws Exception { } // ❌ Daha geniş, HATA!
}5. static, final ve private metotlar override edilemez:
class Parent {
static void staticMethod() { } // override edilemez
final void finalMethod() { } // override edilemez
private void privateMethod() { } // override edilemez (görünmüyor bile)
}Kuralların Özet Tablosu
| Kural | Geçerli mi? |
|---|---|
| Aynı metot ismi ve parametreler | Zorunlu |
| Aynı veya covariant dönüş tipi | Zorunlu |
| Aynı veya daha geniş erişim | Zorunlu |
| Aynı veya daha dar exception | Zorunlu |
| static metot override | ❌ Hayır (hiding olur) |
| final metot override | ❌ Hayır |
| private metot override | ❌ Hayır (görünmez) |
super.method() Kullanımı
Bazen override ederken superclass'ın orijinal davranışını da kullanmak istersin. super.methodName() ile bunu yapabilirsin.
class Employee {
String name;
double salary;
Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
String getDetails() {
return "Name: " + name + ", Salary: " + salary;
}
}
class Manager extends Employee {
String department;
Manager(String name, double salary, String department) {
super(name, salary);
this.department = department;
}
@Override
String getDetails() {
// Önce superclass'ın versiyonunu al, üstüne ekle
return super.getDetails() + ", Dept: " + department;
}
}Manager m = new Manager("Elif", 15000, "Engineering");
System.out.println(m.getDetails());
// Name: Elif, Salary: 15000.0, Dept: Engineeringsuper.getDetails() diyerek Employee'nin getDetails() metodunu çağırıyoruz, sonra kendi bilgimizi ekliyoruz. Bu pattern çok yaygındır — mevcut davranışı koruyup genişletmek.
super Kullanımında Zincirleme
super her zaman bir üst sınıfa erişir. super.super.method() gibi bir şey yazamazsın.
class A {
void hello() { System.out.println("A"); }
}
class B extends A {
@Override
void hello() { System.out.println("B"); }
}
class C extends B {
@Override
void hello() {
super.hello(); // B'nin hello()'sunu çağırır
// super.super.hello() // HATA! Bu geçersiz
}
}Eğer A'nın versiyonuna erişmek istiyorsan, B'nin kendi hello()'sunda super.hello() çağırması gerekir.
toString() Override Etmek
En sık override edilen metotlardan biri Object'ten gelen toString()'dir. Varsayılan hali pek işe yaramaz.
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + "}";
}
}Product p = new Product("Laptop", 15999.99);
System.out.println(p); // Product{name='Laptop', price=15999.99}
System.out.println("Item: " + p); // Item: Product{name='Laptop', price=15999.99}println() ve String birleştirme otomatik olarak toString() çağırır. Override ettiğinde artık anlamlı bir çıktı alırsın.
equals() Override Etmek
Diğer çok sık override edilen metot equals(). Varsayılanı referans karşılaştırması yapar (aynı nesne mi?). Genelde içerik karşılaştırması isteriz.
class Student {
String name;
int id;
Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student other = (Student) obj;
return this.id == other.id && this.name.equals(other.name);
}
@Override
public int hashCode() {
return Objects.hash(name, id);
}
}Student s1 = new Student("Ali", 101);
Student s2 = new Student("Ali", 101);
System.out.println(s1 == s2); // false (farklı nesneler)
System.out.println(s1.equals(s2)); // true (aynı içerik)⚠️ Dikkat:
equals()override ediyorsanhashCode()da override etmelisin. Bu bir kontrat —equals()true dönen iki nesneninhashCode()değerleri aynı olmalı. YoksaHashMap,HashSetgibi yapılar bozulur.
Overriding vs Overloading
Bu iki kavram sürekli karıştırılır. Farkları net olarak bilmelisin.
Overloading — aynı sınıfta aynı isimli ama farklı parametreli metotlar:
class Calculator {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
}Overriding — subclass'ta aynı isimli, aynı parametreli metodu yeniden tanımlama:
class Animal {
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
void speak() { System.out.println("Hav!"); }
}| Özellik | Overloading | Overriding |
|---|---|---|
| Nerede? | Aynı sınıfta (veya kalıtımda) | Subclass'ta |
| Parametreler | Farklı olmalı | Aynı olmalı |
| Dönüş tipi | Farklı olabilir | Aynı veya covariant |
| Binding | Compile-time (static) | Runtime (dynamic) |
@Override | Kullanılmaz | Kullanılır |
En önemli fark: Overloading derleme zamanında, overriding çalışma zamanında çözülür. Bu dynamic binding konusunu ileride detaylı göreceğiz.
Method Hiding (Static Metotlarda)
Static metotlar override edilemez ama gizlenir (hiding). Aradaki fark çok ince ama önemli.
class Parent {
static void greet() {
System.out.println("Hello from Parent");
}
}
class Child extends Parent {
static void greet() { // Bu override değil, hiding!
System.out.println("Hello from Child");
}
}Parent p = new Child();
p.greet(); // "Hello from Parent" — referans tipine bakılır!
Child c = new Child();
c.greet(); // "Hello from Child"Override olsaydı p.greet() "Hello from Child" derdi (çünkü nesne Child). Ama static metotlarda referans tipi belirler — bu hiding.
💡 İpucu: Static metotları sınıf üzerinden çağır, nesne üzerinden değil.
Parent.greet()veChild.greet()daha temiz ve karışıklığı önler.
Gerçekçi Override Örneği: Ödeme Sistemi
class Payment {
double amount;
Payment(double amount) {
this.amount = amount;
}
double calculateFee() {
return 0; // Varsayılan: ücret yok
}
double getTotal() {
return amount + calculateFee();
}
@Override
public String toString() {
return String.format("Amount: %.2f, Fee: %.2f, Total: %.2f",
amount, calculateFee(), getTotal());
}
}
class CreditCardPayment extends Payment {
CreditCardPayment(double amount) {
super(amount);
}
@Override
double calculateFee() {
return amount * 0.025; // %2.5 komisyon
}
}
class BankTransfer extends Payment {
BankTransfer(double amount) {
super(amount);
}
@Override
double calculateFee() {
return 5.0; // Sabit 5 TL
}
}Payment cc = new CreditCardPayment(1000);
System.out.println(cc);
// Amount: 1000.00, Fee: 25.00, Total: 1025.00
Payment bt = new BankTransfer(1000);
System.out.println(bt);
// Amount: 1000.00, Fee: 5.00, Total: 1005.00getTotal() metodu calculateFee() çağırıyor. Her alt sınıf kendi fee hesabını yapıyor. getTotal() override edilmeden doğru çalışıyor çünkü override edilen calculateFee() çağrılıyor.
Override Yaparken Sık Yapılan Hatalar
1. equals() parametresini yanlış yazmak:
// YANLIŞ — bu overload, override değil!
public boolean equals(Student other) { ... }
// DOĞRU — parametre Object olmalı
@Override
public boolean equals(Object other) { ... }2. Erişimi kısıtlamak:
class Parent {
public void doWork() { }
}
class Child extends Parent {
// HATA! public → protected daraltma yapılamaz
// @Override
// protected void doWork() { }
}3. static metodu instance olarak override etmeye çalışmak:
class Parent {
static void util() { }
}
class Child extends Parent {
// HATA! static → non-static dönüşüm yapılamaz
// @Override
// void util() { }
}⚠️ Dikkat: static metodu non-static olarak veya non-static metodu static olarak "override" edemezsin. İkisinin dünyası farklı.
Override Design Patterns
Override sıklıkla şu pattern'lerde kullanılır:
Template Method Pattern: Superclass algoritmanın iskeletini tanımlar, bazı adımları subclass'a bırakır.
class Report {
// Template method
final void generate() {
fetchData();
formatData();
printReport();
}
void fetchData() {
System.out.println("Fetching from database...");
}
void formatData() { } // Subclass override eder
void printReport() {
System.out.println("Printing...");
}
}
class PdfReport extends Report {
@Override
void formatData() {
System.out.println("Formatting as PDF...");
}
}
class ExcelReport extends Report {
@Override
void formatData() {
System.out.println("Formatting as Excel...");
}
}generate() metodu final — kimse değiştiremez. Ama formatData() her subclass'ta farklı. İskelet sabit, detaylar esnek.
Özet
Method overriding, subclass'ın superclass metodunu aynı imzayla yeniden tanımlamasıdır;
@Overrideannotation'ını her zaman kullan.Override kuralları: aynı imza, aynı/covariant dönüş tipi, aynı/daha geniş erişim, aynı/daha dar exception.
super.method() ile superclass'ın orijinal versiyonunu çağırabilirsin — mevcut davranışı genişletmek için kullanışlıdır.
Overriding vs overloading: overriding aynı imza + farklı sınıf (runtime), overloading farklı parametreler + aynı sınıf (compile-time).
static,finalveprivatemetotlar override edilemez; static metotlarda "hiding" kavramı geçerlidir.toString()veequals()en sık override edilen metotlardır;equals()override ediyorsanhashCode()da override et.
AI Asistan
Sorularını yanıtlamaya hazır