Merhabalar, bir önceki yazımızda Java da switch yapısına gelen yeniliklere bir giriş yapmıştık. İlk yazıyı okumak isteyenler blogumdan yazıya ulaşabilir. Bu yazımızda gelen yeniliklere devam edelim.
Yield kullanımı
Bir case içerisinde birden fazla işlem yapmak istediğimizde ve bir değer döndermek istersek nasıl yaparız ? Burada Java 12 ile preview özellik olarak gelen Java 14 ile standart hale gelen yield ile sağlarız.
String sonuc = switch (gun) {
case 1 -> {
System.out.println("İlk gün");
yield "Pazartesi";
}
case 2 -> {
System.out.println("İkinci gün");
yield "Salı";
}
default -> {
System.out.println("Bilinmeyen gün");
yield "Bilinmiyor";
}
};Burada yield “Pazartesi” ifadesi switch expression sonucunu belirler. Burada dikkat edilmesi gereken şey return ile yield aynı şey değildir. return ifadesi metoddan çıkar, yield ise ilgili switch expression’dan bir değer üretir. Örneğin;
public String gunBul(int gun) {
return switch (gun) {
case 1 -> "Pazartesi";
case 2 -> {
System.out.println("Salı seçildi");
yield "Salı";
}
default -> "Bilinmiyor";
};
}Burada yield, switch’in değerini üretirken dışarıdaki return metodun sonucunu döndermektedir.
Switch ile Pattern Matching
Java 21 ile switch çok daha güçlü bir hale bürünüd. Artık case 1, case “JAVA” gibi sabit değerleri kontrol etmek zorunda değiliz. Nesnenin tipine göre işlem yapabiliriz.
Eski yaklaşıma bir örnek;
static void print(Object obj) {
if (obj instanceof String s) {
System.out.println("String: " + s);
} else if (obj instanceof Integer i) {
System.out.println("Integer: " + i);
} else if (obj instanceof Double d) {
System.out.println("Double: " + d);
}
}Pattern matching ile:
static void print(Object obj) {
switch (obj) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
case Double d -> System.out.println("Double: " + d);
default -> System.out.println("Bilinmeyen tip");
}
}Burada Java, hem nesnenin tipini kontrol eder, hemde uygun değişkeni bizim için oluşturur. Örneğin case String s ile eşleşirse, s değişkenini doğrudan kullanabiliriz, ekstra casting yapmamıza gerek kalmayacaktır.
case null kontrolü
Klasik switch yapılarında null değerler özellikle dikkat edilmesi gereken bir konuydu. Modern pattern matching switch ile null doğrudan handle edilebilir. Yeni yapı ile artık if(obj==null) gibi bir kontrol yapmak zorunda değiliz. Örneğin;
static void print(Object obj) {
switch (obj) {
case null -> System.out.println("Değer null");
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
default -> System.out.println("Diğer");
}
}Guarded Pattern ve When Kullanımı
Pattern matching ‘in en güçlü özelliklerinden biri yalnızca tipe değil, tip+ koşula göre eşleşme yapabilmemizdir.
Örneğin;
static void kontrol(Object obj) {
switch (obj) {
case Integer i when i < 0 -> System.out.println("Negatif Integer");
case Integer i when i == 0 -> System.out.println("Sıfır");
case Integer i -> System.out.println("Pozitif Integer");
default -> System.out.println("Integer değil");
}
}Burada, case Integer i when <0 ifadesi ile iki kontrol gerçekleştirilir. Öncelikle obj instanceof Integer ile tip kontrolü yapılır, sonrasında i<0 koşulu değerlendirilir. Her ikisi de koşulu sağlıyorsa ilgili case ifadesi çalışır. Bu yapı özellikle uzun if/else if bloklarının azaltılmasını sağlar.
Case Sıralaması ve Dominance
Pattern matching kullanırken case sıralaması önemlidir. Aşağıdaki örneği inceleyelim:
switch(obj){
case Object o -> System.out.println("Object");
case String s -> System.out.println("String");
}Bu kod problemlidir. Çünkü her String hali hazırda zaten bir Object’tir. Dolayısıyla case Object o önce çalıştığı için; case String s ifadesine hiç bir zaman ulaşılamaz. Doğrusu;
switch(obj){
case String s -> System.out.println("String");
case Object o -> System.out.println("Object");
}Genel mantık: Daha spesifik patternler üstte, daha genel patternler altta olmalıdır.
Default ve Exhaustiveness
Bir switch expression değer üretmek zorunda olduğu için Java tüm olası durumların ele alınmasını ister Örneğin;
int sonuc = switch (sayi) {
case 1 -> 10;
case 2 -> 20;
default -> 0;
};Burada default, diğer tüm değerleri kapsar. Pattern switch’lerde Java exhaustiveness kontolü yapabilir. Örneğin sealed type bir interface düşüneilim:
sealed interface Shape permits Circle, Rectangle {
}
record Circle(double radius) implements Shape {
}
record Rectangle(double width, double height) implements Shape {
}Java Shape için mümkün olan bütün alt tiplerin ele alındığını bildiği için ayrıca default yazmamız gerekmez. Şöyle ki:
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
};Bir sonraki yazımızda görüşmek üzere. Sağlıcakla kalın 🙂
Blog
Bu bölüm, okuyucuları bilgilendirmek ve ilham vermek için çeşitli makaleleri, görüşleri ve kaynakları sergileyen blogun genel bir bakışını sunar.
-

Java’da switch Devrimi: Klasik Switch’ten Pattern Matching’e 2
Merhabalar, bir önceki yazımızda Java da switch yapısına gelen yeniliklere bir giriş yapmıştık. İlk yazıyı…
-

Java’da switch Devrimi: Klasik Switch’ten Pattern Matching’e
Java’nın ilk sürümlerinden beri kullandığımız switch, uzun yıllar boyunca oldukça basit bir kontrol yapısı olarak…
Bir yanıt yazın