Optional ile Null Güvenliği
null, programlamanın en sık hata kaynaklarından biridir; öyle ki onu icat eden Tony Hoare buna "milyar dolarlık hata" demiştir. Bir metot null döndürdüğünde, çağıran taraf bunu kontrol etmeyi unutursa kaçınılmaz sonuç…
Optional ile Null Güvenliği
null, programlamanın en sık hata kaynaklarından biridir; öyle ki onu icat eden Tony Hoare
buna "milyar dolarlık hata" demiştir. Bir metot null döndürdüğünde, çağıran taraf bunu
kontrol etmeyi unutursa kaçınılmaz sonuç NullPointerException'dır. Java 8, bu probleme
zarif bir çözüm getirdi: Optional. Optional, "burada bir değer olabilir ya da
olmayabilir" durumunu tip düzeyinde, açıkça ifade eder.
Optional oluşturma
Üç temel fabrika metodu vardır:
Optional<String> dolu = Optional.of("merhaba"); // değer kesin var (null verilemez) Optional<String> bos = Optional.empty(); // değer yok Optional<String> belki = Optional.ofNullable(deger); // null ise empty olur
Bir metodun "değer bulamayabilir" olduğunu belirtmek için null döndürmek yerine
Optional döndürmek çok daha açıktır; çağıran taraf değerin yokluğunu görmezden gelemez.
Değere güvenli erişim
Optional'ın amacı isPresent()/get() ikilisini elle yazmak değildir (bu, null kontrolünün
süslü hali olurdu). Asıl güç, değerin yokluğunu zarifçe ele alan metotlardadır:
ifPresent(consumer)— değer varsa çalıştırorElse(varsayilan)— yoksa hazır bir değer döndürorElseGet(supplier)— yoksa değeri tembel üret (pahalı varsayılanlar için)orElseThrow(...)— yoksa anlamlı bir exception fırlatifPresentOrElse(varsa, yoksa)— iki ayrı davranış
String ad = bul(id).orElse("bilinmiyor"); Urun u = bul(id).orElseThrow(() -> new NoSuchElementException("yok"));
Örnek 1 (./Ornek1.java) oluşturma ve erişim metotlarını tek tek gösterir.
Dönüşümler: map, filter, flatMap
Optional'ı asıl güçlü kılan, içindeki değeri açmadan dönüştürebilmendir. Değer yoksa
işlemler sessizce atlanır; null kontrolü zincirinden kurtulursun:
map(fn)— değer varsa dönüştürür, yoksa empty kalırfilter(koşul)— koşul sağlanmazsa empty'ye düşürürflatMap(fn)— dönüşümün kendisiOptionaldöndürüyorsa iç içe Optional'ı düzleştirir
Optional.of(" Ada ") .map(String::trim) .filter(s -> !s.isEmpty()) .map(String::toUpperCase) .orElse("(yok)");
Örnek 2 (./Ornek2.java) map/filter/flatMap'i, alanı null olabilen bir kullanıcı
üzerinde gösterir.
Pratik: repository deseni
Optional'ın en doğal kullanımı, "bir şey bulmaya çalışan ama bulamayabilen" metotlardır.
Spring Data JPA'nın findById metodunun Optional döndürmesi tesadüf değildir. Örnek 3
(./Ornek3.java) bir ürün deposunda id'ye göre arama yapar; bulunca alanına erişir,
bulamayınca orElse/orElseThrow ile zarifçe davranır.
Anti-pattern'ler (kaçın!)
optional.get()'i kontrolsüz çağırma — değer yoksaNoSuchElementExceptionatar; bu,nullkontrolünü atlamaktan farksızdır.orElse...ailesini kullan.- Optional'ı alan, metot parametresi veya koleksiyon elemanı yapma. O, esas olarak
dönüş tipi için tasarlandı. (
Optional<List<T>>yerine boş liste döndür.) if (opt.isPresent()) opt.get()kalıbı yerinemap/ifPresent/orElsekullan.
Özet
Optional ile değerin yokluğunu tip düzeyinde açıkça ifade etmeyi, null kontrolü zincirinden
map/filter/flatMap ile kurtulmayı ve orElse/orElseThrow ile zarif varsayılan/hata
davranışı kurmayı öğrendik. Bu, hem daha güvenli hem de daha okunabilir kod demektir. Buraya
kadar dilin çekirdeğini ve modern özelliklerini gördük; sırada dış dünyayla konuşmaya
başlıyoruz: dosya işlemleri ve I/O.
▶ Kod Örnekleri(3)
Ornek1
çalıştırılabilir1// Ornek1: Optional oluşturma ve değere güvenli erişim.
2// Çalıştırma: java Ornek1.java
3import java.util.Optional;
4
5public class Ornek1 {
6
7 public static void main(String[] args) {
8 // Optional, "değer olabilir ya da olmayabilir" durumunu açıkça temsil eder.
9 Optional<String> dolu = Optional.of("merhaba"); // değer kesin var
10 Optional<String> bos = Optional.empty(); // değer yok
11 Optional<String> belkiNull = Optional.ofNullable(null); // null ise empty olur
12
13 System.out.println("dolu.isPresent() : " + dolu.isPresent());
14 System.out.println("bos.isPresent() : " + bos.isPresent());
15 System.out.println("bos.isEmpty() : " + bos.isEmpty());
16
17 // ifPresent: değer varsa çalışır, yoksa hiçbir şey yapmaz (null kontrolüne gerek yok).
18 dolu.ifPresent(v -> System.out.println("\nDeğer var: " + v));
19 bos.ifPresent(v -> System.out.println("Bu satır çalışmaz"));
20
21 // orElse: değer yoksa varsayılan döndür.
22 System.out.println("\ndolu.orElse(...) : " + dolu.orElse("varsayılan"));
23 System.out.println("bos.orElse(...) : " + bos.orElse("varsayılan"));
24 System.out.println("belkiNull.orElse(...) : " + belkiNull.orElse("null geldi"));
25
26 // orElseGet: varsayılanı SADECE gerektiğinde üret (tembel; pahalı hesaplar için).
27 String sonuc = bos.orElseGet(() -> "hesaplanmış varsayılan");
28 System.out.println("bos.orElseGet(...) : " + sonuc);
29
30 // ifPresentOrElse: var/yok için iki ayrı davranış.
31 bos.ifPresentOrElse(
32 v -> System.out.println("değer: " + v),
33 () -> System.out.println("\nDeğer yok, alternatif iş yapıldı.")
34 );
35 }
36}Ornek2
çalıştırılabilir1// Ornek2: Optional dönüşümleri — map, filter, flatMap.
2// Çalıştırma: java Ornek2.java
3import java.util.Optional;
4
5public class Ornek2 {
6
7 public static void main(String[] args) {
8 // map: değer varsa dönüştürür; yoksa empty kalır (null kontrolü gerekmez).
9 Optional<String> ad = Optional.of(" Ada Lovelace ");
10 Optional<String> temiz = ad.map(String::trim).map(String::toUpperCase);
11 System.out.println("map zinciri: " + temiz.orElse("(yok)"));
12
13 Optional<String> bos = Optional.empty();
14 System.out.println("boş.map(...): " + bos.map(String::trim).orElse("(yok)"));
15
16 // filter: koşulu sağlamıyorsa empty'ye düşürür.
17 Optional<Integer> yas = Optional.of(20);
18 String durum = yas.filter(y -> y >= 18)
19 .map(y -> "Reşit (" + y + ")")
20 .orElse("Reşit değil veya bilinmiyor");
21 System.out.println("\nfilter sonucu: " + durum);
22
23 // flatMap: dönüşümün kendisi Optional döndürüyorsa iç içe Optional'ı düzleştirir.
24 Kullanici k = new Kullanici("ada@site.com");
25 Kullanici eksik = new Kullanici(null);
26
27 System.out.println("\nflatMap ile e-posta alanı:");
28 System.out.println(" k -> " + k.eposta().map(e -> e.split("@")[1]).orElse("alan yok"));
29 System.out.println(" eksik -> " + eksik.eposta().map(e -> e.split("@")[1]).orElse("alan yok"));
30 }
31
32 record Kullanici(String adres) {
33 // Alan (adres) null olabilir; dışarıya Optional olarak sunmak daha güvenlidir.
34 Optional<String> eposta() {
35 return Optional.ofNullable(adres);
36 }
37 }
38}Ornek3
çalıştırılabilir1// Ornek3: Gerçekçi senaryo — repository'den arama ve orElseThrow.
2// Çalıştırma: java Ornek3.java
3import java.util.List;
4import java.util.NoSuchElementException;
5import java.util.Optional;
6
7public class Ornek3 {
8
9 record Urun(int id, String ad, double fiyat) {}
10
11 // Basit bir "repository": id'ye göre ürün bulur, bulamazsa Optional.empty döner.
12 // Optional döndürmek, "null dönebilir" demekten çok daha açık ve güvenlidir.
13 static class UrunDeposu {
14 private final List<Urun> urunler = List.of(
15 new Urun(1, "Klavye", 450),
16 new Urun(2, "Mouse", 250),
17 new Urun(3, "Monitör", 3200)
18 );
19
20 Optional<Urun> bul(int id) {
21 return urunler.stream().filter(u -> u.id() == id).findFirst();
22 }
23 }
24
25 public static void main(String[] args) {
26 UrunDeposu depo = new UrunDeposu();
27
28 // Bulunan ürün: map ile alanına eriş.
29 String ad = depo.bul(2).map(Urun::ad).orElse("bilinmiyor");
30 System.out.println("id=2 ürün adı: " + ad);
31
32 // Bulunamayan ürün: orElse ile varsayılan.
33 double fiyat = depo.bul(99).map(Urun::fiyat).orElse(0.0);
34 System.out.println("id=99 fiyat (yoksa 0): " + fiyat);
35
36 // orElseThrow: değer yoksa anlamlı bir hata fırlat.
37 try {
38 Urun u = depo.bul(99)
39 .orElseThrow(() -> new NoSuchElementException("Ürün bulunamadı: id=99"));
40 System.out.println(u);
41 } catch (NoSuchElementException e) {
42 System.out.println("\nİstisna: " + e.getMessage());
43 }
44
45 // Zincirleme: bul -> indirim uygula -> biçimlendir
46 String mesaj = depo.bul(3)
47 .filter(u -> u.fiyat() > 1000)
48 .map(u -> u.ad() + " indirimli fiyat: " + (u.fiyat() * 0.9))
49 .orElse("İndirime uygun ürün yok");
50 System.out.println("\n" + mesaj);
51 }
52}