Java Sealed Class'lar ve Kapsamlı Desen Eşleştirme (Exhaustive Pattern Matching)
Kaynak: Hacker News
İçindekiler 2 başlık
Java 17 ile tanıtılan sealed classes (mühürlü sınıflar), bir arayüzün (interface) veya temel sınıfın izin verilen alt tiplerini açıkça listelemenize olanak tanır. Örneğin, mühürlü bir arayüz ve record yapılarını kullanan basit bir örneğe bakalım (açık bir izin listesi verilmediğinde iç sınıflar örtük olarak izin verilen alt tiplere eklenir):
public sealed interface SealedType {
record TypeA() implements SealedType {}
record TypeB() implements SealedType {}
static SealedType of(String type) {
return switch (type) {
case "A" -> new TypeA();
case "B" -> new TypeB();
default -> throw new IllegalArgumentException();
};
}
}
Cebirsel veri tiplerine (algebraic datatypes) sahip fonksiyonel programlama dillerine aşinaysanız, bunu Haskell veya ML'deki bir veri tipi bildirimine benzetebilirsiniz: data SealedType = TypeA | TypeB. Daha sonra bunu basit bir Main sınıfında şu şekilde kullanabiliriz:
void main(String[] args) {
var val = SealedType.of(args[0]);
System.out.println(switch (val) {
case SealedType.TypeA() -> "A";
case SealedType.TypeB() -> "B";
});
}
Burada dikkat edilmesi gereken önemli nokta, main metodumuzdaki switch ifadesine bir default dalı eklemek zorunda kalmamamızdır. Bunun nedeni, mühürlü sınıfların (ve enum'ların) kapsamlılık kontrolünü (exhaustiveness checking) etkinleştirmesidir: derleyici olası tüm durumların ne olduğunu tam olarak bilir ve hepsini kapsayıp kapsamadığınızı denetleyebilir. Tüm durumları ele aldıysanız default dalına ihtiyacınız yoktur. Bir durumu unutursanız (ve default dalınız yoksa), derleme zamanı hatası alırsınız.
Bu özellik, bir tipin tüm kullanımlarının olası her durumu kapsamasından emin olmak istediğinizde harikadır; ancak yeni bir tür geriye dönük uyumsuz değişiklik (breaking change) ortaya çıkarır: mühürlü bir sınıfa veya arayüze yeni bir alt tip eklemek, bu kodu kullanan istemcileri bozabilir. Örneğin, örneğimize yeni bir TypeC durumu eklemek, eksik durum nedeniyle main metodunun derlenmemesine yol açacaktır. Dolayısıyla, API'nizde mühürlü bir tipi dışa açıyorsanız, yeni bir alt tip eklemek SemVer (Anlamsal Sürümleme) takip ediyorsanız ana sürüm (major version) artışını gerektiren kırıcı bir değişikliktir.
Derleme Zamanı Hatası mı, Çalışma Zamanı Hatası mı?#
Java, tüketici kodu (bu örnekte main) derlediğinizde kapsamlı olmayan bir switch için derleme zamanı hatası üretse de, mühürlü tip değiştiğinde tüketici yeniden derlenmezse bunu yapamaz. Örneğin, SealedType yapımızı başka bir durumla genişlettiğimizi varsayalım:
public sealed interface SealedType {
record TypeA() implements SealedType {}
record TypeB() implements SealedType {}
record TypeC() implements SealedType {}
static SealedType of(String type) {
return switch (type) {
case "A" -> new TypeA();
case "B" -> new TypeB();
case "C" -> new TypeC();
default -> throw new IllegalArgumentException();
};
}
}
Eğer sadece SealedType.java dosyasını yeniden derler ve Main sınıfını yeniden derlemezsek, yeni durumu tetiklediğimizde bir çalışma zamanı istisnası (runtime exception) ile karşılaşırız:
% java Main C
Exception in thread "main" java.lang.MatchException
at Main.main(Main.java:3)
Burada yeni MatchException hatasının fırlatıldığını görüyoruz. Javadoc belgeleri, ayrı derleme (separate compilation) kaynaklı bu potansiyel soruna ve desenlerdeki null değerlerle ilgili bazı uç durumlara dikkat çekmektedir. Dolayısıyla, mühürlü sınıfları kullanmanın yeni bir durum eklendiğinde tüm tüketicileri güncellemeyi statik olarak garanti edeceğini umuyorsanız, her şeyi yeniden derlemediğiniz sürece bu geçerli değildir.
Sonuçlar ve Pratik Çıkarımlar#
Buradan çıkarılabilecek en temel sonuç, mühürlü tiplerin (sealed types) muhtemelen bir bileşenin iç 구현 (implementation) katmanında en faydalı olduğu, ancak bileşenin sunduğu açık API (public API) içinde dışarıya açıldığında daha az kullanışlı olduğudur.
Yazılım geliştirme pratiği açısından şu çıkarımları dikkate almak faydalı olacaktır:
- Modül İçi Güvenlik: Aynı derleme birimi veya mikroservis içinde domain modellerini temsil ederken
sealedarayüzler harika bir tip güvenliği sağlar. - Kütüphane Tasarımı: Dış dünyaya açılan SDK veya kütüphanelerde
sealedtipler kullanmak, gelecekteki genişletmeleri zorlaştırır ve ikili uyumluluk (binary compatibility) aşamasındaMatchExceptionriskini doğurur. Açık API'lerde tüketicileridefaultdalı kullanmaya teşvik etmek veya geleneksel arayüz tasarımlarına bağlı kalmak daha güvenli bir yaklaşımdır.
Kaynak ve atıf
Bu yazı, Hacker News üzerinde yayımlanan içerikten yararlanılarak Edumints tarafından yapay zekâ desteğiyle Türkçe hazırlandı. Ayrıntılar ve yazarların kendi anlatımı için orijinaline bakabilirsin:
- neilmadden.blog/2026/04/24/java-sealed-classes-and-exhaustive-pattern-matching