← Kursa Dön
📄 Text · 12 min

Method Overloading

Giriş — Aynı İsim, Farklı Parametre

Diyelim ki bir topla metodu yazdın. İki int topluyorsun. Ama sonra üç int toplamak istedin. Bir de double toplamak istedin. Her birine farklı isim mi vereceksin? toplaIkiInt, toplaUcInt, toplaDouble?

Hayır! Java'da aynı isimli birden fazla metot tanımlayabilirsin — yeter ki parametreleri farklı olsun. Buna method overloading (metot aşırı yükleme) denir.

Analoji: "Aç" kelimesini düşün. "Kapıyı aç", "dosyayı aç", "şişeyi aç" — aynı fiil ama farklı nesnelere uygulanıyor ve her biri farklı bir eylem. Java'da da topla(int, int) ve topla(double, double) aynı isim ama farklı işlem.


Overloading Temelleri

Overloading için metotların imzaları farklı olmalı. İmza = metot adı + parametre tipleri ve sırası.

public class Toplama {

    // 2 int toplama
    public static int topla(int a, int b) {
        return a + b;
    }

    // 3 int toplama
    public static int topla(int a, int b, int c) {
        return a + b + c;
    }

    // 2 double toplama
    public static double topla(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        System.out.println(topla(3, 5));         // 8   → topla(int, int)
        System.out.println(topla(3, 5, 7));      // 15  → topla(int, int, int)
        System.out.println(topla(3.5, 2.1));     // 5.6 → topla(double, double)
    }
}

Java, çağrıdaki argüman tiplerini ve sayısını bakarak hangi metodu çağıracağını otomatik belirler. Bu işlem derleme zamanında (compile-time) gerçekleşir.

Overloading İçin Yeterli Farklar

  1. Parametre sayısı farklı → overload

  2. Parametre tipleri farklı → overload

  3. Parametre sırası farklı → overload (ama dikkatli ol!)

void metot(int a, String b) { }    // metot(int, String)
void metot(String a, int b) { }    // metot(String, int) — farklı imza

Overloading İçin Yetersiz Farklar

  1. Sadece dönüş tipi farklı → overload OLMAZ

  2. Sadece parametre isimleri farklı → overload OLMAZ

// DERLEME HATASI — sadece dönüş tipi farklı
int topla(int a, int b) { return a + b; }
double topla(int a, int b) { return a + b; }  // Aynı imza!

// DERLEME HATASI — sadece parametre isimleri farklı
void yazdir(String mesaj) { }
void yazdir(String text) { }  // Aynı imza: yazdir(String)

💡 İpucu: Overloading, "aynı işi farklı tiplerle yapan" metotlar için kullan. Tamamen farklı işler yapan metotlara farklı isimler ver.


Overloading Örnekleri

Örnek: print/println

Java'nın kendi println metodu overloaded:

System.out.println(42);           // println(int)
System.out.println(3.14);         // println(double)
System.out.println("Merhaba");    // println(String)
System.out.println(true);         // println(boolean)
System.out.println('A');          // println(char)

Aynı isim, farklı tipler. Hepsini ayrı isimle çağırmak zorunda olsaydın: printlnInt, printlnDouble, printlnString... Korkunç olurdu.

Örnek: Geometrik Alan Hesaplama

public class AlanHesapla {

    // Kare alanı
    public static double alan(double kenar) {
        return kenar * kenar;
    }

    // Dikdörtgen alanı
    public static double alan(double en, double boy) {
        return en * boy;
    }

    // Üçgen alanı
    public static double alan(double taban, double yukseklik, boolean ucgenMi) {
        return taban * yukseklik / 2;
    }

    public static void main(String[] args) {
        System.out.println("Kare: " + alan(5));           // 25.0
        System.out.println("Dikdörtgen: " + alan(5, 3));  // 15.0
        System.out.println("Üçgen: " + alan(6, 4, true)); // 12.0
    }
}

Her alan() çağrısı parametre sayısına göre doğru metoda yönlendirilir.

Örnek: Formatlı Yazdırma

public static void bilgiYazdir(String isim) {
    System.out.println("İsim: " + isim);
}

public static void bilgiYazdir(String isim, int yas) {
    System.out.println("İsim: " + isim + ", Yaş: " + yas);
}

public static void bilgiYazdir(String isim, int yas, String sehir) {
    System.out.println("İsim: " + isim + ", Yaş: " + yas + ", Şehir: " + sehir);
}

public static void main(String[] args) {
    bilgiYazdir("Ali");
    bilgiYazdir("Ayşe", 25);
    bilgiYazdir("Mehmet", 30, "İstanbul");
}

Overload Resolution — Java Nasıl Karar Veriyor?

Birden fazla overloaded metot varsa, Java en uygun olanını seçer. Bu sürece overload resolution denir.

1. Tam Eşleşme (Exact Match)

void metot(int x) { System.out.println("int"); }
void metot(double x) { System.out.println("double"); }

metot(5);    // "int" — tam eşleşme
metot(5.0);  // "double" — tam eşleşme

2. Otomatik Genişleme (Widening)

Tam eşleşme yoksa Java, daha geniş bir tipe otomatik dönüşüm yapar:

void metot(double x) { System.out.println("double"); }

metot(5);  // "double" — int → double genişleme

intdouble otomatik genişleme (widening) yapılır çünkü veri kaybı olmaz.

Genişleme sırası:

byte → short → int → long → float → double
char → int → long → float → double

3. Birden Fazla Uygun Metot — Ambiguity

void metot(int a, double b) { }
void metot(double a, int b) { }

metot(5, 5);  // DERLEME HATASI — ambiguous!

Java ikisi arasında karar veremez çünkü her iki metoda da dönüşüm gerekli. Bu durumda cast yaparak belirsizliği çözersin:

metot(5, (double) 5);  // metot(int, double) çağrılır
metot((double) 5, 5);  // metot(double, int) çağrılır

4. Autoboxing ve Unboxing

Java 5+ ile primitive ↔ wrapper otomatik dönüşüm yapılır:

void metot(int x) { System.out.println("primitive int"); }
void metot(Integer x) { System.out.println("wrapper Integer"); }

metot(5);       // "primitive int" — tam eşleşme öncelikli
metot(Integer.valueOf(5)); // "wrapper Integer"

Tam eşleşme her zaman autoboxing'ten önceliklidir.

Öncelik Sırası

1. Tam eşleşme (exact match)
2. Genişleme (widening)
3. Autoboxing/Unboxing
4. Varargs (en son)

⚠️ Dikkat: Bu kuralları ezberleme. Karmaşık overloading senaryolarında Java'nın ne yapacağını tahmin etmek zor olabilir. Şüphe duyduğunda cast yap veya farklı isim ver.


Varargs (Değişken Sayıda Argüman)

Ya 2, ya 5, ya 100 parametre göndermek istersen? Her biri için ayrı overload mu yazacaksın? Hayır — varargs (variable arguments) bunu çözer.

public static int topla(int... sayilar) {
    int toplam = 0;
    for (int s : sayilar) {
        toplam += s;
    }
    return toplam;
}

public static void main(String[] args) {
    System.out.println(topla());            // 0
    System.out.println(topla(5));           // 5
    System.out.println(topla(1, 2, 3));     // 6
    System.out.println(topla(1, 2, 3, 4, 5)); // 15
}

int... sayilar ifadesi, sıfır veya daha fazla int argüman kabul eder. Metot içinde sayilar bir dizi gibi davranır.

Varargs Kuralları

  1. Her metotta en fazla bir varargs parametresi olabilir

  2. Son parametre olmalı

// DOĞRU — varargs sonda
void metot(String mesaj, int... sayilar) { }

// YANLIŞ — varargs sonda değil
void metot(int... sayilar, String mesaj) { }  // Derleme hatası!

// YANLIŞ — iki varargs
void metot(int... a, double... b) { }  // Derleme hatası!

Örnek: Loglamac

public static void log(String seviye, String... mesajlar) {
    System.out.print("[" + seviye + "] ");
    for (String mesaj : mesajlar) {
        System.out.print(mesaj + " ");
    }
    System.out.println();
}

public static void main(String[] args) {
    log("INFO", "Sistem başlatıldı");
    log("ERROR", "Bağlantı", "hatası", "oluştu");
    log("DEBUG", "x=5", "y=10", "z=15", "toplam=30");
}

Varargs ve Dizi

Varargs aslında arka planda bir dizi oluşturur. Dizi de gönderebilirsin:

int[] notlar = {90, 85, 78};

// Her ikisi de çalışır:
topla(1, 2, 3);      // varargs
topla(notlar);        // dizi

Varargs ve Overloading

Varargs overloading'de en düşük önceliğe sahiptir:

void metot(int a, int b) { System.out.println("iki parametre"); }
void metot(int... a) { System.out.println("varargs"); }

metot(1, 2);  // "iki parametre" — tam eşleşme öncelikli
metot(1, 2, 3); // "varargs" — sadece bu eşleşir
metot(1);     // "varargs"

💡 İpucu: Varargs kullanışlıdır ama aşırı kullanma. Eğer genellikle 2-3 parametreyle çağrılıyorsa, o sayılar için ayrı overload yaz ve varargs'ı "geri kalan" için tut. Bu hem okunabilirliği hem performansı artırır (varargs her çağrıda dizi oluşturur).


Overloading ve Constructors

Constructor'lar (yapıcı metotlar) da overloaded olabilir. Bu, nesneleri farklı şekillerde oluşturmayı sağlar. OOP dersinde detaylı göreceğiz ama kısa bir önizleme:

public class Dikdortgen {
    double en, boy;
    
    // Parametresiz — varsayılan değerler
    public Dikdortgen() {
        this.en = 1;
        this.boy = 1;
    }
    
    // Kare — tek parametre
    public Dikdortgen(double kenar) {
        this.en = kenar;
        this.boy = kenar;
    }
    
    // Dikdörtgen — iki parametre
    public Dikdortgen(double en, double boy) {
        this.en = en;
        this.boy = boy;
    }
}

// Kullanım
Dikdortgen d1 = new Dikdortgen();         // 1x1
Dikdortgen d2 = new Dikdortgen(5);        // 5x5
Dikdortgen d3 = new Dikdortgen(5, 3);     // 5x3

Overloading vs Overriding

Bu iki kavram sık karıştırılır. Şimdilik kısa bir karşılaştırma yapalım (overriding'i OOP derslerinde göreceksin):

ÖzellikOverloadingOverriding
Aynı sınıfta mı?EvetHayır (alt sınıfta)
Metot adıAynıAynı
ParametrelerFarklı olmalıAynı olmalı
Dönüş tipiFarklı olabilirAynı (veya alt tip)
Bağlama zamanıDerleme (compile-time)Çalışma (runtime)
static?OlabilirOlamaz

Overloading = "aynı isim, farklı parametreler, derleme zamanında çözülür" Overriding = "aynı metot, alt sınıfta yeniden tanımlanır, çalışma zamanında çözülür"


Pratik Örnek: String Formatlama Yardımcıları

public class StringUtil {

    // Sola hizala
    public static String padLeft(String metin, int uzunluk) {
        return padLeft(metin, uzunluk, ' ');
    }

    // Sola hizala — özel karakter
    public static String padLeft(String metin, int uzunluk, char dolgu) {
        if (metin.length() >= uzunluk) return metin;
        
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < uzunluk - metin.length(); i++) {
            sb.append(dolgu);
        }
        sb.append(metin);
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(padLeft("42", 5));       // "   42"
        System.out.println(padLeft("42", 5, '0'));  // "00042"
    }
}

İlk metot ikincisini çağırıyor — varsayılan değer simülasyonu. Bu çok yaygın bir overloading desenidir: basit versiyonu çağır, o da detaylı versiyona yönlendirir.

Pratik Örnek: Mesaj Oluşturucu

public static String mesajOlustur(String alici) {
    return mesajOlustur(alici, "Merhaba");
}

public static String mesajOlustur(String alici, String selamlama) {
    return mesajOlustur(alici, selamlama, null);
}

public static String mesajOlustur(String alici, String selamlama, String imza) {
    StringBuilder sb = new StringBuilder();
    sb.append(selamlama).append(", ").append(alici).append("!");
    if (imza != null) {
        sb.append("\n\n-- ").append(imza);
    }
    return sb.toString();
}

public static void main(String[] args) {
    System.out.println(mesajOlustur("Ali"));
    // Merhaba, Ali!
    
    System.out.println(mesajOlustur("Ali", "Selam"));
    // Selam, Ali!
    
    System.out.println(mesajOlustur("Ali", "Selam", "Mehmet"));
    // Selam, Ali!
    //
    // -- Mehmet
}

Bu "telescoping constructor" desenidir — her basit versiyon bir sonrakini çağırır. İşe yarar ama çok fazla parametre olunca Builder pattern daha uygun olur (ileri seviye).


Overloading Tasarım Desenleri

Desen 1: Varsayılan Parametre Simülasyonu

Java'da default parameter yoktur (Python'daki gibi). Overloading ile bunu simüle edebilirsin:

public static String formatTarih(int gun, int ay, int yil) {
    return formatTarih(gun, ay, yil, "/");
}

public static String formatTarih(int gun, int ay, int yil, String ayirici) {
    return String.format("%02d%s%02d%s%04d", gun, ayirici, ay, ayirici, yil);
}

// Kullanım
formatTarih(15, 3, 2024);          // "15/03/2024"
formatTarih(15, 3, 2024, "-");     // "15-03-2024"
formatTarih(15, 3, 2024, ".");     // "15.03.2024"

Desen 2: Tip Dönüşüm Kolaylığı

public static boolean arasindaMi(int deger, int alt, int ust) {
    return deger >= alt && deger <= ust;
}

public static boolean arasindaMi(double deger, double alt, double ust) {
    return deger >= alt && deger <= ust;
}

public static boolean arasindaMi(char karakter, char alt, char ust) {
    return karakter >= alt && karakter <= ust;
}

// Kullanım
arasindaMi(5, 1, 10);         // true
arasindaMi(3.14, 3.0, 4.0);   // true
arasindaMi('M', 'A', 'Z');    // true

Aynı mantık farklı tiplere uygulanıyor. Kullanıcı tip dönüşümü düşünmek zorunda kalmıyor.

Desen 3: Convenience Methods (Kolaylık Metotları)

// Tam versiyon
public static void log(String seviye, String mesaj, boolean zaman, String dosya) {
    StringBuilder sb = new StringBuilder();
    if (zaman) {
        sb.append("[").append(java.time.LocalDateTime.now()).append("] ");
    }
    sb.append("[").append(seviye).append("] ").append(mesaj);
    System.out.println(sb);
}

// Kolaylık metotları
public static void logInfo(String mesaj) {
    log("INFO", mesaj, true, null);
}

public static void logError(String mesaj) {
    log("ERROR", mesaj, true, null);
}

public static void logDebug(String mesaj) {
    log("DEBUG", mesaj, false, null);
}

Bu desende tüm iş tek bir ana metotta yapılır, kolaylık metotları onu uygun parametrelerle çağırır.


Gerçek Dünya: Java Standart Kütüphanede Overloading

Java'nın kendi kütüphanesi overloading'i yoğun kullanır:

String.valueOf()

String.valueOf(42);        // "42"    — valueOf(int)
String.valueOf(3.14);      // "3.14"  — valueOf(double)
String.valueOf(true);      // "true"  — valueOf(boolean)
String.valueOf('A');       // "A"     — valueOf(char)
String.valueOf(new int[]{1,2}); // "[I@..." — valueOf(Object)

Math sınıfı

Math.abs(-5);      // 5   — abs(int)
Math.abs(-5L);     // 5L  — abs(long)
Math.abs(-5.0f);   // 5.0f — abs(float)
Math.abs(-5.0);    // 5.0 — abs(double)

Math.max(3, 5);    // 5   — max(int, int)
Math.max(3.0, 5.0); // 5.0 — max(double, double)

Arrays sınıfı

Arrays.sort(intDizi);       // sort(int[])
Arrays.sort(doubleDizi);    // sort(double[])
Arrays.sort(stringDizi);    // sort(Object[])
Arrays.sort(dizi, 0, 5);   // sort(int[], int, int) — aralık belirtmeli

Overloading ile İlgili İleri Seviye Notlar

Generic Metotlar (Kısa Bakış)

Java Generics ile tek bir metot birden fazla tipi karşılayabilir. Bu bazen overloading'e alternatiftir:

// Overloading ile
public static int enBuyuk(int a, int b) { return a > b ? a : b; }
public static double enBuyuk(double a, double b) { return a > b ? a : b; }
public static String enBuyuk(String a, String b) { return a.compareTo(b) > 0 ? a : b; }

// Generic ile (tek metot)
public static <T extends Comparable<T>> T enBuyuk(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

Generics'i ilerideki derslerde öğreneceksin. Şimdilik bilmen gereken: bazen tek generic metot, birden fazla overload'dan daha iyi bir çözüm olabilir.

Overloading ve null

void metot(String s) { System.out.println("String"); }
void metot(Integer i) { System.out.println("Integer"); }

metot(null);  // DERLEME HATASI — ambiguous!

null hem String hem Integer olabilir. Java karar veremez. Cast ile çözersin:

metot((String) null);   // "String"
metot((Integer) null);  // "Integer"

Yaygın Hatalar

1. Sadece Dönüş Tipi Farklı

// DERLEME HATASI
int hesapla(int x) { return x * 2; }
double hesapla(int x) { return x * 2.0; }

Dönüş tipi imzanın parçası değil. İmza aynı olduğu için overloading olmaz.

2. Autoboxing Karmaşası

void metot(Integer x) { System.out.println("Integer"); }
void metot(long x) { System.out.println("long"); }

metot(5);  // "long" — genişleme, autoboxing'ten öncelikli!

int → long genişleme, int → Integer autoboxing'ten önce gelir. Bu beklenmedik olabilir.

3. Varargs Ambiguity

void metot(int... a) { }
void metot(int a, int... b) { }

metot(1, 2);  // DERLEME HATASI — ambiguous!

Her iki metot da metot(1, 2) çağrısını karşılayabilir. Java karar veremez.


Özet

  • Method overloading: Aynı isimli, farklı parametreli metotlar tanımlama — Java derleme zamanında doğru metodu seçer.

  • Farklılık parametrelerde olmalı: tip, sayı veya sıralama. Sadece dönüş tipi veya parametre isimleri yeterli değil.

  • Overload resolution önceliği: tam eşleşme → genişleme → autoboxing → varargs.

  • Varargs (int... args) değişken sayıda argüman kabul eder. Son parametre olmalı, metot başına bir tane.

  • Varsayılan değer simülasyonu için basit overload'un detaylı versiyonu çağırmasını sağla.

  • Aşırı overloading'den kaçın — belirsiz durumlar (ambiguity) zor debug edilir. Şüphede farklı isim ver.

  • Varsayılan parametre simülasyonu: Basit overload'un detaylı versiyonu çağırması en yaygın ve en temiz desen.

  • Java kütüphaneleri overloading'i yoğun kullanır: println, String.valueOf, Math.abs, Arrays.sort hep overloaded.

Ne Zaman Overloading Yapma

Overloading güçlü ama her zaman doğru çözüm değil:

  1. Metotlar farklı işler yapıyorsa — Farklı isim ver. hesaplaVergi() ve hesaplaKar() iki farklı iş, ikisine de hesapla() deme.

  2. Parametre anlamı belirsizleşiyorsaolustur(String, String) iki string alıyor ama hangisi isim, hangisi soyisim? Belirsiz overloading'den kaçın.

  3. 5+ overload varsa — Bu kadar çok overload varsa, muhtemelen Builder pattern veya bir konfigürasyon nesnesi daha uygun.

Alıştırma Fikirleri

  1. maks(int, int), maks(int, int, int), maks(int[]) overload'ları yaz

  2. formatSayi(int)"42", formatSayi(int, int)"0042" (padding), formatSayi(double, int)"3.14" (decimal places)

  3. birlesir(String, String), birlesir(String, String, String), birlesir(String...) ile string birleştirme metotları yaz