Optional'ı Doğru Kullanmak — Yokluğu Tipe Yazmak
Önce şunu oku: Lambda ve Fonksiyonel Arayüzler — Lambda Neyi Yakalar?
30 saniyede özet
Optional, 'bu metot bazen eli boş döner' demenin kibar yoludur. Zincir kurarsan her adımdaki boşluğu kendisi taşır. Dikkat: orElse'e verdiğin yedek her seferinde hazırlanır, orElseGet'inki yalnızca gerekince.
Uygulama her istekte başka bir servise gidip şehir soruyor. Oysa kullanıcıların neredeyse hepsinin şehri zaten kayıtlı. Suçlu tek kelime: orElse.
Adım adım oku
- orElse ile yedek sipariş en baştan verilir: servis, asıl değer olsa da olmasa da çağrılır.
- Asıl değer, yani kayıtlı şehir gelir. Yedek servisin cevabı boşuna hazırlanmıştır ve çöpe gider.
- orElseGet ise yedeği bir tarif olarak saklar. Yalnızca asıl değer yoksa çalıştırır.
- Asıl değer geldiği için yedek servis bu kez hiç aranmadı.
-
Bayt: Uygulamam her istekte başka bir servise gidip şehir soruyor.
-
Sen: Ama kullanıcıların şehri zaten kayıtlı değil mi?
-
Bayt: Kayıtlı! Yine de her seferinde yedek sipariş veriliyor.
-
Bayt: Tek kelimeyi değiştir, yedek restoran yalnızca gerçekten gerekince aransın.
Optional ne işe yarar?
findById bazen eli boş döner. Eskiden bu null ile anlatılırdı ve metodun imzasında görünmezdi. OptionalDeğer içeren ya da boş olan bir kutu. Bir metodun bazen sonuç döndürmeyebileceğini dönüş tipine yazar; çağıran boş durumu ele almadan değere ulaşamaz.Sözlükte gör → ise “burada bir şey olmayabilir” bilgisini dönüş tipine yazar; çağıran boş durumu düşünmeden değere ulaşamaz.
| Kullanım | Uygun mu | Neden |
|---|---|---|
Optional<User> findById(id) | Evet | Tasarlandığı yer |
Alan: Optional<String> nickname | Hayır | Serializable değil; alan zaten null olabilir |
Parametre: send(Optional<Email>) | Hayır | Çağıranı sarmalamaya zorlar; iki metot yaz |
Optional<List<Order>> | Hayır | Boş liste zaten “yok” demek |
Kafam karıştı, daha basit anlat
Optional bir kutudur: içinde bir şey olabilir ya da boş olabilir. Metot bu kutuyu verdiğinde, “bakmadan açma” diye etiket de vermiş olur.
Optional en çok hangi yerde işe yarar?
Her kullanımı Optional için uygun ya da uygun değil olarak ayır.
get yerine zincir kur
user.isPresent() ? user.get().address().city() : 'Ankara' — kullanıcı var ama adresi null. Ne olur? Cevabı göster
NullPointerException. isPresent() yalnızca dıştaki User’ı kontrol etti. İçerideki null adres, düz bir null kontrolündeki gibi gözden kaçtı.
isPresent() ile get()’i birlikte kullanmak, eski null kontrolünü uzun yoldan yeniden yazmaktır. map ise her adımda karşılaştığı null’ı boş bir Optional’a çevirir ve zinciri kazasız sonuna kadar taşır.
String city = user.map(User::address) .map(Address::city) .orElse("Ankara");Zincirdeki adım zaten bir Optional döndürüyorsa map yerine flatMap kullan; yoksa kutunun içinde kutu (Optional<Optional<User>>) elde edersin. Değerin mutlaka orada olmasını bekliyorsan get() yerine orElseThrow() yaz: aynı işi yapar, ama niyetin okunur.
Kafam karıştı, daha basit anlat
map zincirinde her adım kutuyu açar ve içine bakar. Bir adımda boşluk çıkarsa zincir sessizce durur, sonunda yedek değer gelir.
`Optional<User> findManager(User u)` metodu var. `user.map(this::findManager)` ifadesinin tipi nedir ve ne yazılmalıydı?
Java 10 ile gelen argümansız `orElseThrow()`, `get()` ile nasıl karşılaştırılır?
Satır satır: orElse ne zaman hesaplanır
orElse sıradan bir metottur ve Java bir metoda vereceği şeyi, metodu çağırmadan önce hazırlar. Yani yedek değer hiç gerekmese bile hesaplanır; buna eager evaluationBir ifadeyi ihtiyaç olup olmadığına bakmadan hemen hesaplamak. Java metot argümanlarını metot çağrılmadan önce hesaplar; orElse(x()) bu yüzden x()'i her zaman çalıştırır.Sözlükte gör → denir.
Değer varken orElse
Optional<String> city = Optional.of("İzmir");String a = city.orElse(defaultCity());String b = city.orElseGet(() -> defaultCity());Debug
main Optional dolu.
- city
- = Optional[İzmir]
Sol/sağ ok tuşlarıyla da gezebilirsin.
Yedek sabit bir değerse orElse gayet iyi. Yedeği hazırlamak için bir metot, veritabanı ya da ağ çağrısı gerekiyorsa orElseGet kullan: yedek yalnızca gerektiğinde hazırlanır. Buna lazy evaluationİşi gerçekten ihtiyaç duyulana kadar ertelemek. Stream'de ara işlemler terminal işlem gelene kadar hiç çalışmaz.Sözlükte gör → denir.
Program ne yazdırır?
Kendin gör
Optional — değer yoksa ne olur, varsa ne boşa gider?
Tohum 59141Optional<User> user = users.findById(id);String city = user .map(User::address) // null dönerse boş Optional .map(Address::city) .orElse(defaultCity());Oynat ya da adımla.
Şu an ne oldu?
Kullanıcının şehrini oku; yoksa varsayılanı kullan
defaultCity() bir config servisine gidiyor. Hangi durumda çağrılıyor, hangi durumda boşuna?
Görevler0/3
Doğru sonuç al ama varsayılanı boşuna hesaplataçık
İpucu
Kullanıcının şehri var; varsayılanın argümanına bak.
Optional kullanırken NullPointerException alaçık
İpucu
İçerideki bir alan null olsun.
Adresi null olan kullanıcı için exception almadan varsayılan şehri döndüraçık
İpucu
Her adımda null'ı boşluğa çeviren erişim.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Sonuç doğru, ama
defaultCity()boşuna çağrıldı. - Varsayılanı
orElseGetyap. Çağrı kayboldu. - Kullanıcıyı “adresi null”, erişimi
isPresent() + get()yap. NullPointerException. - Erişimi map zincirine döndür. Aynı kullanıcı için varsayılan şehir geldi.
orElseThrow()seç. Hata “değer yok” diyor; hangi adımın boş olduğunu söylemiyor.
Kafam karıştı, daha basit anlat
orElse yedeği her zaman hazırlar, gerekmese bile. orElseGet ise yedeği yalnızca kutu boşsa hazırlar. Yedek pahalıysa ikincisini seç.
Simülatörde kullanıcı var ama adresi null. `isPresent() + get()` NullPointerException verdi, map zinciri vermedi. Fark nereden geliyor?
Tuzaklar
Optional döndüren metotta return null. Çağıranın tek güvencesini bozar; Optional.empty() dön.
Optional.of(x), x null ise hemen patlar. Null olabilecek değer için ofNullable.
Yokluk bir hataysa, ne olduğunu söyle. orElseThrow(() -> new UserNotFound(id)), “No value present” mesajından çok daha fazlasını anlatır.
Her null için Optional. Metot içindeki yerel bir değişken için basit bir if daha okunurdur.
Bu metodu çağıran kod `findCoupon(code).isPresent()` satırında NullPointerException alıyor. Hatalı satır hangisi?
Aşağıdaki örnek bir bankanın ‘arkadaşını getir’ kampanyasından ve Optional’ı yalnızca olması gereken yerde kullanıyor: “olmayabilir” diyen dönüş tipi olarak. Alanlarda ve parametrelerde yok; zincir map ve flatMap ile kuruluyor, sınırda anlamlı bir hataya dönüşüyor.
Derinleş · Bankanın 'arkadaşını getir' kampanyası: Optional'ın doğru yeri 5 dosya · ~84 satır · ilk okumada atlayabilirsin
Kendini sına
orElse'e verilen ifade, Optional dolu olsa bile hesaplanır.
`Optional.of(repository.byCode(code))` satırı, kupon bulunamadığında ne yapar?
Aklında kalacak üç şey
- 1 Optional bir dönüş tipidir: 'bulunamayabilir' sonucunu metodun imzasına yazar. Alan, parametre ve koleksiyon için kullanılmaz; boş liste zaten 'yok' demektir.
- 2 map ve flatMap zinciri her adımdaki yokluğu kendisi taşır. isPresent() ve get() ile elle kontrol etmek aynı null kontrolünü uzun yoldan yazar.
- 3 orElse'e verilen yedek, değer varken bile hesaplanır. Yedek pahalıysa ya da yan etkisi varsa orElseGet kullanılır.
5 kart sonraki derste seni bekliyor