← Kursa Dön
📄 Text · 15 min

switch-case ve Pattern Matching

Giriş — Neden switch?

Bir önceki derste if-else ile karar vermeyi öğrendin. Ama bir düşün: bir değişkenin değerine göre 7-8 farklı yola gitmek istiyorsan, else if zinciri hem uzun hem de tekrarlı oluyor. Hep aynı değişkeni kontrol ediyorsun, sadece karşılaştırılan değer değişiyor.

İşte switch tam bu durum için tasarlanmış. Bir değişkenin farklı değerlerine göre farklı kodlar çalıştırır — daha temiz, daha okunaklı.

Analoji: switch'i bir asansör gibi düşün. Bir düğmeye basıyorsun (değer), asansör direkt o kata gidiyor. if-else ise merdiven gibi — her katı tek tek kontrol ediyorsun "bu kat mı, bu kat mı?" diye.


Klasik switch Yapısı

Java'nın orijinal switch söz dizimi şöyle:

switch (ifade) {
    case değer1:
        // değer1 ise çalışır
        break;
    case değer2:
        // değer2 ise çalışır
        break;
    default:
        // hiçbiri eşleşmezse çalışır
        break;
}

ifade bir değer üretir, Java bu değeri sırayla case'lerle karşılaştırır. Eşleşen case'in kodunu çalıştırır. break olmadan bir sonraki case'e "düşer" (fall-through) — birazdan bunu göreceğiz.

Desteklenen Tipler

switch ifadesinde kullanabileceğin tipler:

  • byte, short, char, int (ve bunların wrapper'ları: Byte, Short, Character, Integer)

  • String (Java 7+)

  • enum

Kullanamadıkların: long, float, double, boolean. Bunlar switch'te desteklenmez.

Örnek: Günün Adını Bulma

int gun = 3;

switch (gun) {
    case 1:
        System.out.println("Pazartesi");
        break;
    case 2:
        System.out.println("Salı");
        break;
    case 3:
        System.out.println("Çarşamba");
        break;
    case 4:
        System.out.println("Perşembe");
        break;
    case 5:
        System.out.println("Cuma");
        break;
    case 6:
        System.out.println("Cumartesi");
        break;
    case 7:
        System.out.println("Pazar");
        break;
    default:
        System.out.println("Geçersiz gün!");
        break;
}

gun 3 olduğu için case 3 eşleşir, "Çarşamba" yazdırılır ve break ile switch'ten çıkılır.

Örnek: String ile switch

String komut = "start";

switch (komut) {
    case "start":
        System.out.println("Sistem başlatılıyor...");
        break;
    case "stop":
        System.out.println("Sistem durduruluyor...");
        break;
    case "restart":
        System.out.println("Sistem yeniden başlatılıyor...");
        break;
    default:
        System.out.println("Bilinmeyen komut: " + komut);
        break;
}

String karşılaştırması büyük/küçük harfe duyarlıdır. "Start" ile "start" farklıdır. Gerekirse önce .toLowerCase() yapabilirsin.


Fall-through — break Unutursan Ne Olur?

break ifadesi switch'ten çıkışı sağlar. Eğer break yazmazsan, Java eşleşen case'den sonra tüm case'leri sırayla çalıştırır — ta ki bir break'e veya switch'in sonuna ulaşana kadar. Buna fall-through denir.

int gun = 3;

switch (gun) {
    case 1:
        System.out.println("Pazartesi");
    case 2:
        System.out.println("Salı");
    case 3:
        System.out.println("Çarşamba");
    case 4:
        System.out.println("Perşembe");
    case 5:
        System.out.println("Cuma");
    default:
        System.out.println("---");
}

Çıktı:

Çarşamba
Perşembe
Cuma
---

case 3 eşleşti ama break olmadığı için 4, 5 ve default da çalıştı. Bu genellikle bir hatadır.

⚠️ Dikkat: Fall-through, klasik switch'in en tehlikeli özelliğidir. Her case'e break koymayı unutma! IDE'ler genellikle bunu uyarı olarak gösterir.

Fall-through Kasıtlı Kullanım

Nadiren fall-through kasıtlı olarak kullanılır — birden fazla case'in aynı kodu çalıştırmasını istediğinde:

int ay = 7;
String mevsim;

switch (ay) {
    case 12:
    case 1:
    case 2:
        mevsim = "Kış";
        break;
    case 3:
    case 4:
    case 5:
        mevsim = "İlkbahar";
        break;
    case 6:
    case 7:
    case 8:
        mevsim = "Yaz";
        break;
    case 9:
    case 10:
    case 11:
        mevsim = "Sonbahar";
        break;
    default:
        mevsim = "Geçersiz ay";
        break;
}

System.out.println(mevsim); // Yaz

Burada case 6, 7 ve 8 aynı kodu paylaşıyor. Bu, fall-through'nun kabul edilebilir tek kullanımıdır.


default Bloğu

default, hiçbir case eşleşmediğinde çalışır. if-else'teki son else gibi düşünebilirsin.

char harf = 'X';

switch (harf) {
    case 'A':
        System.out.println("Mükemmel");
        break;
    case 'B':
        System.out.println("İyi");
        break;
    case 'C':
        System.out.println("Orta");
        break;
    default:
        System.out.println("Tanınmayan not: " + harf);
        break;
}

default zorunlu değildir ama her zaman yaz. Beklemediğin değerleri yakalamanı sağlar. Onu yazmamak, olası hataları gizlemek demektir.

💡 İpucu: default'u her zaman en sona koy. Teknik olarak herhangi bir yere koyabilirsin ama en sonda olması okunabilirlik açısından en iyisidir.


Enum ile switch

switch ve enum birlikte harika çalışır. Hatta Java, enum switch'te tüm case'leri kapsamadığını tespit edebilir:

enum Renk { KIRMIZI, YESIL, MAVI }

Renk secim = Renk.YESIL;

switch (secim) {
    case KIRMIZI:
        System.out.println("Dur!");
        break;
    case YESIL:
        System.out.println("Geç!");
        break;
    case MAVI:
        System.out.println("Bilgi");
        break;
}

Dikkat: case'lerde Renk.KIRMIZI değil, sadece KIRMIZI yazıyoruz. switch zaten enum tipini biliyor.

Örnek: Hesap Makinesi

enum Islem { TOPLA, CIKAR, CARP, BOL }

double a = 10, b = 3;
Islem islem = Islem.BOL;
double sonuc;

switch (islem) {
    case TOPLA:
        sonuc = a + b;
        break;
    case CIKAR:
        sonuc = a - b;
        break;
    case CARP:
        sonuc = a * b;
        break;
    case BOL:
        if (b == 0) {
            System.out.println("Sıfıra bölünemez!");
            return;
        }
        sonuc = a / b;
        break;
    default:
        sonuc = 0;
        break;
}

System.out.printf("%.1f %s %.1f = %.2f%n", a, islem, b, sonuc);

Enhanced switch (Java 14+) — Arrow Syntax

Java 14'te switch ifadesi büyük bir güncelleme aldı. Arrow syntax (->) ile yazmak artık mümkün. Bu yeni syntax'ın en büyük avantajı: break'e gerek yok ve fall-through yok!

int gun = 3;

switch (gun) {
    case 1 -> System.out.println("Pazartesi");
    case 2 -> System.out.println("Salı");
    case 3 -> System.out.println("Çarşamba");
    case 4 -> System.out.println("Perşembe");
    case 5 -> System.out.println("Cuma");
    case 6 -> System.out.println("Cumartesi");
    case 7 -> System.out.println("Pazar");
    default -> System.out.println("Geçersiz gün!");
}

Ne kadar temiz değil mi? break yok, fall-through riski yok. Her case tam olarak bir şey yapıyor.

Birden Fazla Değer Aynı Case'de

int ay = 7;

switch (ay) {
    case 12, 1, 2  -> System.out.println("Kış");
    case 3, 4, 5   -> System.out.println("İlkbahar");
    case 6, 7, 8   -> System.out.println("Yaz");
    case 9, 10, 11 -> System.out.println("Sonbahar");
    default         -> System.out.println("Geçersiz ay");
}

Eski syntax'ta 3 case alt alta yazıyorduk, şimdi virgülle ayırıyoruz. Çok daha okunaklı.

Blok Kullanımı

Eğer bir case'de birden fazla satır çalıştırmak istersen süslü parantez kullan:

int gun = 6;

switch (gun) {
    case 1, 2, 3, 4, 5 -> {
        System.out.println("İş günü");
        System.out.println("Erken kalk!");
    }
    case 6, 7 -> {
        System.out.println("Hafta sonu");
        System.out.println("Keyfine bak!");
    }
    default -> System.out.println("Geçersiz");
}

switch Expressions — Değer Döndüren switch

Java 14+ ile switch artık bir ifade (expression) olarak da kullanılabilir. Yani switch'ten bir değer döndürebilirsin:

int gun = 3;

String gunAdi = switch (gun) {
    case 1 -> "Pazartesi";
    case 2 -> "Salı";
    case 3 -> "Çarşamba";
    case 4 -> "Perşembe";
    case 5 -> "Cuma";
    case 6 -> "Cumartesi";
    case 7 -> "Pazar";
    default -> "Geçersiz";
};

System.out.println(gunAdi); // Çarşamba

Dikkat: sonunda noktalı virgül (;) var çünkü artık bu bir atama ifadesi.

switch Expression ile Mevsim

int ay = 11;

String mevsim = switch (ay) {
    case 12, 1, 2  -> "Kış";
    case 3, 4, 5   -> "İlkbahar";
    case 6, 7, 8   -> "Yaz";
    case 9, 10, 11 -> "Sonbahar";
    default         -> "Geçersiz";
};

System.out.println(ay + ". ay → " + mevsim);

yield Anahtar Kelimesi

Eğer switch expression'da blok kullanıyorsan, değeri yield ile döndürürsün (return değil!):

int puan = 78;

String not = switch (puan / 10) {
    case 10, 9 -> "AA";
    case 8 -> "BA";
    case 7 -> "BB";
    case 6 -> "CB";
    case 5 -> "CC";
    default -> {
        if (puan < 0 || puan > 100) {
            yield "Geçersiz puan";
        } else {
            yield "FF";
        }
    }
};

System.out.println("Not: " + not);

yield sadece switch expression içindeki bloklarda kullanılır. Normal switch statement'ta kullanılamaz.

💡 İpucu: switch expression kullanıyorsan, Java tüm olası değerlerin kapsandığını kontrol eder. Enum kullanıyorsan ve tüm değerleri yazdıysan default yazmana gerek kalmayabilir. Ama int/String için default zorunludur.


Klasik vs Enhanced switch Karşılaştırma

Aynı kodu iki şekilde yazalım:

Klasik (eski)

String mesaj;
int seviye = 2;

switch (seviye) {
    case 1:
        mesaj = "Başlangıç";
        break;
    case 2:
        mesaj = "Orta";
        break;
    case 3:
        mesaj = "İleri";
        break;
    default:
        mesaj = "Bilinmeyen";
        break;
}

Enhanced (yeni)

int seviye = 2;

String mesaj = switch (seviye) {
    case 1 -> "Başlangıç";
    case 2 -> "Orta";
    case 3 -> "İleri";
    default -> "Bilinmeyen";
};

Fark ortada. Yeni syntax:

  • Daha kısa

  • Fall-through riski yok

  • Değer döndürebilir

  • Daha okunabilir

Tavsiye: Java 14+ kullanıyorsan (ki modern projelerde kullanıyorsundur), her zaman enhanced switch tercih et.


Pattern Matching (Java 17+ Preview, Java 21+ Final)

Java 21 ile switch'e pattern matching geldi. Bu, switch'in sadece sabit değerleri değil, tipleri de kontrol edebilmesini sağlar.

Eski Yöntem — instanceof Zinciri

Object obj = "Merhaba";

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("String, uzunluk: " + s.length());
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    System.out.println("Integer, değer: " + i);
} else if (obj instanceof Double) {
    Double d = (Double) obj;
    System.out.println("Double, değer: " + d);
} else {
    System.out.println("Bilinmeyen tip");
}

Bu çok tekrarlı. Her seferinde instanceof kontrol edip cast yapıyorsun.

Yeni Yöntem — Pattern Matching switch

Object obj = "Merhaba";

switch (obj) {
    case String s  -> System.out.println("String, uzunluk: " + s.length());
    case Integer i -> System.out.println("Integer, değer: " + i);
    case Double d  -> System.out.println("Double, değer: " + d);
    default        -> System.out.println("Bilinmeyen tip");
}

Çok daha temiz! case String s ifadesi hem tip kontrolü yapıyor hem de cast'i otomatik hallediyor. s değişkeni direkt String tipinde kullanılabilir.

Guarded Patterns — when ile Koşul Ekleme

Pattern'e ek koşul eklemek için when kullanabilirsin (Java 21+):

Object obj = 42;

String sonuc = switch (obj) {
    case Integer i when i > 0  -> "Pozitif tam sayı: " + i;
    case Integer i when i == 0 -> "Sıfır";
    case Integer i             -> "Negatif tam sayı: " + i;
    case String s when s.isEmpty() -> "Boş string";
    case String s              -> "String: " + s;
    case null                  -> "null değer";
    default                    -> "Diğer: " + obj;
};

System.out.println(sonuc);

when ile pattern'e ek koşul ekliyorsun. Bu, if-else'i switch içine taşımış gibi.

null Handling

Geleneksel switch'te null değer NullPointerException fırlatır. Pattern matching switch'te null'ı yakalayabilirsin:

String deger = null;

switch (deger) {
    case null  -> System.out.println("null geldi!");
    case "abc" -> System.out.println("abc");
    default    -> System.out.println("Diğer: " + deger);
}

⚠️ Dikkat: Pattern matching switch'te case sıralaması önemlidir. Daha spesifik pattern'ler önce gelmelidir. case Integer i when i > 0 her zaman case Integer i'den önce olmalı, aksi halde derleme hatası alırsın.


Sealed Classes ile Exhaustive switch

Java 17+ sealed class'larla pattern matching switch'i birleştirince çok güçlü bir yapı ortaya çıkar:

sealed interface Sekil permits Daire, Dikdortgen, Ucgen {}

record Daire(double yaricap) implements Sekil {}
record Dikdortgen(double en, double boy) implements Sekil {}
record Ucgen(double taban, double yukseklik) implements Sekil {}

double alanHesapla(Sekil sekil) {
    return switch (sekil) {
        case Daire d       -> Math.PI * d.yaricap() * d.yaricap();
        case Dikdortgen dk -> dk.en() * dk.boy();
        case Ucgen u       -> u.taban() * u.yukseklik() / 2;
    };
}

Dikkat: default yok! Çünkü Sekil sealed ve tüm alt tipler kapsanmış. Java bunu derleme zamanında doğrular. Yarın yeni bir Sekil alt tipi eklenirse, bu switch derleme hatası verir — böylece güncellemeyi unutamazsın.


switch vs if-else — Hangisini Kullanalım?

DurumTercih
Bir değişkenin sabit değerlerini kontrolswitch
Aralık kontrolü (>, <, >=)if-else
Enum değerleriswitch
boolean koşullarif-else
Tip kontrolü (Java 21+)switch (pattern matching)
2-3 koşulif-else (daha basit)
4+ sabit değerswitch (daha okunaklı)
// if-else daha uygun — aralık kontrolü
if (sicaklik > 30) {
    System.out.println("Sıcak");
} else if (sicaklik > 15) {
    System.out.println("Ilık");
} else {
    System.out.println("Soğuk");
}

// switch daha uygun — sabit değerler
String sonuc = switch (httpKodu) {
    case 200 -> "OK";
    case 404 -> "Not Found";
    case 500 -> "Internal Server Error";
    default  -> "Unknown";
};

Yaygın Hatalar

1. break Unutmak (Klasik switch)

// HATALI — her case'den sonra fall-through olur
switch (x) {
    case 1:
        System.out.println("Bir");
        // break unuttun!
    case 2:
        System.out.println("İki"); // x=1 olsa bile çalışır!
}

Çözüm: Enhanced switch kullan veya her case'e break koy.

2. Değişken Tanımlama Scope Sorunu

// HATALI — case'ler aynı scope'ta
switch (x) {
    case 1:
        String mesaj = "Bir";  // scope sorunu!
        break;
    case 2:
        String mesaj = "İki";  // Derleme hatası: zaten tanımlı!
        break;
}

// ÇÖZÜM — süslü parantez ile scope oluştur
switch (x) {
    case 1: {
        String mesaj = "Bir";
        System.out.println(mesaj);
        break;
    }
    case 2: {
        String mesaj = "İki";
        System.out.println(mesaj);
        break;
    }
}

Enhanced switch'te (->) bu sorun yok çünkü her case otomatik olarak kendi scope'unda.

3. null Değer (Klasik switch)

String s = null;

// PATLAR — NullPointerException!
switch (s) {
    case "a":
        break;
}

Klasik switch null kaldıramaz. Ya önceden null kontrolü yap, ya da Java 21+ pattern matching switch kullan.


Pratik Örnek: Basit Menü Sistemi

import java.util.Scanner;

Scanner scanner = new Scanner(System.in);

System.out.println("=== Ana Menü ===");
System.out.println("1. Yeni Oyun");
System.out.println("2. Devam Et");
System.out.println("3. Ayarlar");
System.out.println("4. Çıkış");
System.out.print("Seçiminiz: ");

int secim = scanner.nextInt();

switch (secim) {
    case 1 -> {
        System.out.println("Yeni oyun başlatılıyor...");
        System.out.println("Zorluk seviyesi seçin:");
        System.out.println("  1-Kolay  2-Orta  3-Zor");
    }
    case 2 -> System.out.println("Kayıtlı oyun yükleniyor...");
    case 3 -> System.out.println("Ayarlar açılıyor...");
    case 4 -> System.out.println("Hoşça kalın!");
    default -> System.out.println("Geçersiz seçim! 1-4 arası girin.");
}

Pratik Örnek: HTTP Durum Kodu Açıklama

int httpKodu = 404;

String aciklama = switch (httpKodu) {
    case 200 -> "OK — İstek başarılı";
    case 201 -> "Created — Kaynak oluşturuldu";
    case 204 -> "No Content — İçerik yok";
    case 301 -> "Moved Permanently — Kalıcı yönlendirme";
    case 302 -> "Found — Geçici yönlendirme";
    case 400 -> "Bad Request — Hatalı istek";
    case 401 -> "Unauthorized — Yetkilendirme gerekli";
    case 403 -> "Forbidden — Erişim yasak";
    case 404 -> "Not Found — Sayfa bulunamadı";
    case 500 -> "Internal Server Error — Sunucu hatası";
    case 502 -> "Bad Gateway — Geçit hatası";
    case 503 -> "Service Unavailable — Hizmet kullanılamıyor";
    default  -> "Bilinmeyen HTTP kodu: " + httpKodu;
};

System.out.println(httpKodu + ": " + aciklama);

Record Pattern Matching (Java 21+)

Java 21 ile sadece tipi değil, record'un içindeki alanları da doğrudan switch'te açabilirsin (destructuring):

record Nokta(int x, int y) {}

Object obj = new Nokta(3, 4);

switch (obj) {
    case Nokta(int x, int y) when x == 0 && y == 0 -> 
        System.out.println("Orijin noktası");
    case Nokta(int x, int y) when x == 0 -> 
        System.out.println("Y ekseni üzerinde, y=" + y);
    case Nokta(int x, int y) when y == 0 -> 
        System.out.println("X ekseni üzerinde, x=" + x);
    case Nokta(int x, int y) -> 
        System.out.println("Nokta(" + x + ", " + y + ")");
    default -> 
        System.out.println("Nokta değil");
}

Record pattern matching ile iç içe record'ları bile açabilirsin:

record Adres(String sehir, String ilce) {}
record Kisi(String ad, Adres adres) {}

Object obj = new Kisi("Ali", new Adres("İstanbul", "Kadıköy"));

switch (obj) {
    case Kisi(String ad, Adres(String sehir, String ilce)) ->
        System.out.println(ad + " → " + sehir + "/" + ilce);
    default ->
        System.out.println("Kişi değil");
}
// Ali → İstanbul/Kadıköy

Bu özellik özellikle fonksiyonel programlama stilinde çok güçlü. Veri yapılarını switch ile zarif bir şekilde işleyebilirsin.


switch ile Performans

switch-case, compiler tarafından optimize edilir. Ardışık int değerleri için Java tableswitch (dizi tabanlı atlama tablosu), dağınık değerler için lookupswitch (binary search) bytecode'u üretir. Bu, uzun if-else zincirlerinden genellikle daha hızlıdır.

Ama performans farkı çoğu durumda ihmal edilebilir düzeydedir. switch'i okunabilirlik için tercih et, performans için değil.

// Compiler bunu tableswitch ile optimize eder — O(1)
switch (gun) {
    case 1 -> "Pazartesi";
    case 2 -> "Salı";
    // ...
}

// Bu da if-else zinciri — worst case O(n)
if (gun == 1) return "Pazartesi";
else if (gun == 2) return "Salı";
// ...

Özet

  • Klasik switch (case: ... break;) eski syntax — fall-through riski var, her zaman break koy.

  • Enhanced switch (case ... ->) Java 14+ — fall-through yok, daha temiz syntax, bunu tercih et.

  • switch expression değer döndürebilir — yield ile blok içinden değer döndürürsün.

  • Pattern matching (Java 21+) switch'te tip kontrolü yapabilirsin — case String s -> gibi.

  • `default` her zaman yaz — beklenmeyen değerleri yakalamak için.

  • switch vs if-else: Sabit değer kontrollerinde switch, aralık kontrollerinde if-else tercih et.