---
title: '`Optional`'
source: 'https://academia.sh/tr/kurslar/java-standart-kutuphane/optional'
course: 'Standart Kütüphane ve Akışlar'
language: tr
updated: '2026-08-17T18:09:42+00:00'
license: 'CC BY-SA 4.0'
---

# `Optional`

Yokluğu tiple ifade etmek çağıranın yükümlülüğünü derleme zamanına taşımaz: dört denetimsiz çağrının dördü de — boş değer dönen imzada da, Optional dönen imzada da — denetimsiz derlenir ve biri çalışma zamanında düşer. Kazanç imzada görünürlüktür; bu kazanç alanda ve parametrede kayboluyor, ve of() boş değerde hemen düşerken ofNullable() düşmüyor.

Önceki ders bir akış zincirinin sonunda biriken kabın hangi sözlerle kurulduğunu ölçtü.
Toplama her zaman bir değer üretir — ama tek bir öğe **bulmak** istendiğinde, kaynakta o
öğe hiç yoksa ne olur? Standart kütüphanenin bazı yöntemleri bu durumda boş değer döndürür,
bazıları `Optional<T>` denen, yokluğu **tiple** ifade eden bir kap döndürür. `Optional`
kendisi bir arayüz değil, tek bir sınıftır; kursun "garantiyi kim veriyor" sorusu burada
üçüncü bir biçimde sorulur: yokluğu tipe taşımak, çağıranın "önce denetle, sonra oku"
yükümlülüğünü gerçekten ortadan kaldırıyor mu, yoksa yalnızca **görünür** mü kılıyor?
Yokluk tipe taşındığında çağıranın yükümlülüğü derleme zamanına mı geçiyor, yoksa denetim
yine çalışma zamanında mı kalıyor?

## Aynı İş, İki İmza: Denetim Yine Çalışma Zamanındadır

- **FN27** — Aynı arama işi iki imzayla yazılır: biri boş değer döndürebilen bir `String`,
  öteki bir `Optional<String>`. İkisi de var olan ve olmayan birer girdiyle, hem denetimsiz
  hem güvenli idyomla çağrılır.

```java
// Ayni.java — ayni is bos deger donen imza ile Optional donen imzada cagirana kalan denetim
import java.util.*;

public class Ayni {
    static final Map<String, String> DEPO = Map.of("armut", "yesil");

    static String renkBul(String meyve) { return DEPO.get(meyve); }
    static Optional<String> renkBulO(String meyve) { return Optional.ofNullable(DEPO.get(meyve)); }

    public static void main(String[] args) {
        int denetimliCagri = 0, denetimsizCagri = 0;

        try {
            int u1 = renkBul("armut").length();
            denetimsizCagri++;
            System.out.println("bos deger donen imza, var olan girdi : " + u1 + " (denetimsiz derlendi)");
        } catch (NullPointerException e) { System.out.println("beklenmedik istisna"); }

        try {
            int u2 = renkBul("elma").length();
            System.out.println("bos deger donen imza, olmayan girdi  : " + u2);
        } catch (NullPointerException e) {
            denetimsizCagri++;
            System.out.println("bos deger donen imza, olmayan girdi  -> istisna: "
                    + e.getClass().getSimpleName() + " (denetimsiz derlendi, calisma zamaninda dustu)");
        }

        try {
            int u3 = renkBulO("armut").get().length();
            denetimsizCagri++;
            System.out.println("Optional donen imza, var olan girdi  : " + u3 + " (denetimsiz derlendi)");
        } catch (NoSuchElementException e) { System.out.println("beklenmedik istisna"); }

        try {
            int u4 = renkBulO("elma").get().length();
            System.out.println("Optional donen imza, olmayan girdi   : " + u4);
        } catch (NoSuchElementException e) {
            denetimsizCagri++;
            System.out.println("Optional donen imza, olmayan girdi   -> istisna: "
                    + e.getClass().getSimpleName() + " (denetimsiz derlendi, calisma zamaninda dustu)");
        }

        System.out.println("guvenli idyom, bos deger donen imza  : "
                + (renkBul("elma") != null ? renkBul("elma") : "bilinmiyor"));
        denetimliCagri++;
        System.out.println("guvenli idyom, Optional donen imza   : " + renkBulO("elma").orElse("bilinmiyor"));
        denetimliCagri++;

        System.out.println();
        System.out.println("denetimsiz derlenen cagri sayisi (dort cagrinin dordu de): " + denetimsizCagri);
        System.out.println("denetimli (guvenli idyomla yazilan) cagri sayisi         : " + denetimliCagri);
    }
}
```

```
bos deger donen imza, var olan girdi : 5 (denetimsiz derlendi)
bos deger donen imza, olmayan girdi  -> istisna: NullPointerException (denetimsiz derlendi, calisma zamaninda dustu)
Optional donen imza, var olan girdi  : 5 (denetimsiz derlendi)
Optional donen imza, olmayan girdi   -> istisna: NoSuchElementException (denetimsiz derlendi, calisma zamaninda dustu)
guvenli idyom, bos deger donen imza  : bilinmiyor
guvenli idyom, Optional donen imza   : bilinmiyor

denetimsiz derlenen cagri sayisi (dort cagrinin dordu de): 4
denetimli (guvenli idyomla yazilan) cagri sayisi         : 2
```

Dört denetimsiz çağrının **dördü de** derleniyor — imza `String` olsun `Optional<String>`
olsun, derleyici hiçbirine itiraz etmiyor. Olmayan girdiyle çağrıldıklarında ikisi de
çalışma zamanında düşüyor, yalnızca istisnanın adı değişiyor: `NullPointerException` yerine
`NoSuchElementException`. Ölçülen sayı burada nettir: aynı işi yapan iki imzanın çağırana
bıraktığı, denetimsiz geçilebilecek çağrı sayısı **eşittir**. `Optional` dönüş tipi bu
sayıyı sıfıra indirmiyor; denetim yine yazılmadığında yine çalışma zamanında düşüyor.

Kazanç başka bir yerdedir: **imzanın kendisinde**. `String renkBul(String)` imzasını okuyan
biri, dönen değerin boş olabileceğini bilemez — bunu yalnızca belgeden ya da kaynağı okuyarak
öğrenir. `Optional<String> renkBulO(String)` imzası ise yokluk olasılığını doğrudan
söyler; çağıran bunu görmek için ne belgeye ne kaynağa bakmak zorundadır. Denetimin kendisi
hâlâ isteğe bağlıdır, ama denetimin **gerekip gerekmediği** artık tipte yazılıdır.

Bu ayrım Nesneye Dayalı Java kursunda kurulan denetlenen istisna ile karşılaştırılabilir:
orada bir yöntemin fırlatabileceği istisna imzada yazılıydı ve çağıran onu ele almak
**zorundaydı**, aksi hâlde derleme durmuyordu — yakalamamak da bir seçenekti, ama seçenek
en azından görünürdü. `Optional` dönüş tipi benzer bir görünürlük sağlıyor, ama zorunluluk
tarafı yoktur: denetlenen istisnayı hiç ele almayan kod derlenmezken, bir `Optional`'ı hiç
denetlemeden `get()` çağıran kod dördüncü satırda görüldüğü gibi sorunsuz derleniyor. İki
düzenek de yokluğu ya da hatayı imzaya taşıyor; yalnızca biri bunu zorunlu kılıyor, öteki
yalnızca görünür kılıyor.

## Zincirleme Denetimi Ortadan Kaldırmaz, Erteler

`Optional` yalnız bir kap değildir; `map`, `orElse`, `orElseGet` gibi yöntemlerle akış
zincirine benzer bir zincirleme sunar. Bu zincirleme boş değeri hiç yazılmamış bir denetime
dönüştürmez, ama en azından her adımda tekrarlanan bir `if (x != null)` bloğunu ortadan
kaldırır.

- **FN28** — Aynı iki girdiyle (`"armut"`, `"elma"`) `renkBulO` çağrılır ve sonuç `map` ile
  büyük harfe çevrilip `orElse` ile varsayılan bir dizgiye bağlanır; ara adımlarda hiç açık
  denetim yazılmaz.

```java
// Zincir.java — Optional zincirlemesi ara denetimi nasil kaldiriyor
import java.util.*;

public class Zincir {
    static final Map<String, String> DEPO = Map.of("armut", "yesil");
    static Optional<String> renkBulO(String meyve) { return Optional.ofNullable(DEPO.get(meyve)); }

    public static void main(String[] args) {
        String buyukVarOlan = renkBulO("armut").map(s -> s.toUpperCase(Locale.ROOT)).orElse("YOK");
        String buyukOlmayan = renkBulO("elma").map(s -> s.toUpperCase(Locale.ROOT)).orElse("YOK");
        System.out.println("var olan girdi, zincirin sonucu   : " + buyukVarOlan);
        System.out.println("olmayan girdi, zincirin sonucu    : " + buyukOlmayan);
    }
}
```

```
var olan girdi, zincirin sonucu   : YESIL
olmayan girdi, zincirin sonucu    : YOK
```

`map(s -> s.toUpperCase(Locale.ROOT))` çağrısı, kap boşsa hiç çalışmaz — `Predicate` ve
`Function` derslerinde ölçülen "arayüz garantisi" burada da geçerlidir: `map`'in kendi
sözü, boş bir kap üzerinde işlev hiç çağırmamaktır. Bu yüzden `renkBulO("elma")` boş kap
döndürdüğünde `s.toUpperCase(...)` hiç çalışmıyor ve bir istisna düşmüyor; zincir doğrudan
`orElse("YOK")`'a düşüyor. Kazanç burada gerçek: aynı işi boş değerle yazsaydık, her adımda
ayrı bir `if` gerekirdi.

Ama bu zincirlemenin kendi bir tuzağı var. `orElse` ile `orElseGet` aynı işi yapıyor
görünür — ikisi de "yoksa şunu ver" der — fakat argümanlarını **ne zaman** hesapladıkları
farklıdır.

- **FN29** — Aynı pahalı varsayılan değer üretici hem dolu hem boş bir kapta, hem
  `orElse` hem `orElseGet` ile çağrılır; üreticinin kaç kez çalıştığı sayılır.

```java
// OrElse.java — orElse pahali varsayilani her zaman hesaplar, orElseGet yalnizca gerektiginde
import java.util.*;

public class OrElse {
    static int cagriSayaci = 0;

    static String pahaliVarsayilan() {
        cagriSayaci++;
        return "varsayilan";
    }

    public static void main(String[] args) {
        Optional<String> dolu = Optional.of("armut");

        cagriSayaci = 0;
        dolu.orElse(pahaliVarsayilan());
        System.out.println("dolu kapta orElse(), varsayilan kac kez cagrildi   : " + cagriSayaci);

        cagriSayaci = 0;
        dolu.orElseGet(OrElse::pahaliVarsayilan);
        System.out.println("dolu kapta orElseGet(), varsayilan kac kez cagrildi: " + cagriSayaci);

        Optional<String> bos = Optional.empty();
        cagriSayaci = 0;
        bos.orElse(pahaliVarsayilan());
        System.out.println("bos kapta orElse(), varsayilan kac kez cagrildi    : " + cagriSayaci);

        cagriSayaci = 0;
        bos.orElseGet(OrElse::pahaliVarsayilan);
        System.out.println("bos kapta orElseGet(), varsayilan kac kez cagrildi : " + cagriSayaci);
    }
}
```

```
dolu kapta orElse(), varsayilan kac kez cagrildi   : 1
dolu kapta orElseGet(), varsayilan kac kez cagrildi: 0
bos kapta orElse(), varsayilan kac kez cagrildi    : 1
bos kapta orElseGet(), varsayilan kac kez cagrildi : 1
```

`orElse` dört satırın **ikisinde de** bir kez çağrılıyor — kap dolu olsa bile. Bunun
nedeni sözdizimindedir: `orElse(pahaliVarsayilan())` yazıldığında `pahaliVarsayilan()`
çağrısı bir Java yöntem çağrısının argümanı olarak, `orElse`'in kendisi çalışmadan **önce**
değerlendirilir — kap dolu olsa bile üretici çalışır, sonucu `orElse` yalnızca kullanmaz.
`orElseGet(OrElse::pahaliVarsayilan)` ise bir üretici **referansı** alır ve onu yalnızca
kap boşken çağırır; dolu kapta üretici hiç çalışmıyor. Bu, önceki konudaki tembellik
ölçümünün burada tekrarıdır: bir işlemin ne zaman çalıştığı, o işlemi saran yöntemin adına
bakılarak değil, koşturularak anlaşılır.

## Kazanç Alanda ve Parametrede Kayboluyor

`Optional`'ın kazandırdığı görünürlük, yalnızca dönüş tipi olarak kullanıldığında işler.
Bir alan ya da parametre tipi olarak kullanıldığında aynı kazanım kayboluyor.

- **FN30** — `Optional<String>` tipinde bir alan taşıyan bir sınıf kurulur ve o alana
  doğrudan boş değer atanır — derleyici buna izin verir, çünkü `Optional` da referans
  tipli sıradan bir sınıftır.

```java
// Alan.java — Optional bir alanda kullanilinca kazanc yok oluyor mu
import java.util.*;

public class Alan {
    static class Kayit {
        Optional<String> etiket;
    }

    public static void main(String[] args) {
        Kayit k = new Kayit();
        k.etiket = null;
        System.out.println("alanin kendisi bos deger olabiliyor mu: " + (k.etiket == null));
        try {
            boolean var = k.etiket.isPresent();
            System.out.println("alan uzerinde isPresent(): " + var);
        } catch (NullPointerException e) {
            System.out.println("alan uzerinde isPresent() -> istisna: " + e.getClass().getSimpleName());
        }
    }
}
```

```
alanin kendisi bos deger olabiliyor mu: true
alan uzerinde isPresent() -> istisna: NullPointerException
```

`Optional<String> etiket` alanına doğrudan `null` atamak **derleniyor**. `Optional` bir
tip parametresi taşıyan sıradan bir sınıftır; kendisi de bir referanstır ve her referans
gibi boş olabilir. Alan boşken `isPresent()` çağırmak bu kez `NullPointerException`
düşürüyor — tam olarak, `Optional`'ın önlemek için var olduğu istisnanın aynısı, yalnızca
bir kat dışarıda.

Bu, dersin sınırlayıcı ölçümüdür: `Optional`'ın kazancı yalnızca bir yöntemin **dönüş
tipi** olarak kullanıldığı dar bir bağlamda geçerlidir. Bir alanda ya da bir parametrede
kullanıldığında iki kat yokluk olasılığı doğar — alanın kendisi boş olabilir, alanın
taşıdığı değer de boş olabilir — ve kazanılan görünürlük, kazandığından fazlasını geri
alan bir karmaşıklığa dönüşür. Standart kütüphanenin kendi belgesi bu kullanımı önerilmez
sayar; burada ölçülen, o önerinin gerekçesidir.

Nedeni birinci bölümdeki ölçümle birleştiğinde netleşiyor: `Optional`'ın tek koruması
imzada duran bir **hatırlatmadır**, çalışma zamanında uygulanan bir kural değil. Dönüş
tipi olarak kullanıldığında bu hatırlatma her çağrı yerinde yeniden görünür — yöntemi her
çağıran, dönen değerin `Optional` olduğunu görür ve karar verir. Alan olarak kullanıldığında
ise hatırlatma yalnız sınıfın **tanımında** bir kez görünür; sınıfı kullanan kod alana her
eriştiğinde o hatırlatmayı yeniden okumaz, çünkü erişim bir alan okuması olarak görünür,
`Optional`'ın kendisi görünmez hâle gelir. Görünürlüğün kazancı, tam olarak görünürlüğün
kaybolduğu yerde biter.

## `of` Hemen Düşer, `ofNullable` Düşmez

`Optional` bir kap kurmanın iki ana yolu var: `Optional.of(deger)` ve
`Optional.ofNullable(deger)`. İkisi de aynı tipte bir kap döndürür, ama boş değerle
çağrıldıklarında ayrışırlar.

- **FN31** — `Optional.of(null)` ve `Optional.ofNullable(null)` ayrı ayrı denenir;
  ikincisinin sonucundan `get()` çağrılarak denetimin ne zaman düştüğü ölçülür.

```java
// OfOfNullable.java — of hemen dusuyor, ofNullable dusmuyor
import java.util.*;

public class OfOfNullable {
    public static void main(String[] args) {
        try {
            Optional<String> o = Optional.of(null);
            System.out.println("of(null) kurulurken: dustu");
        } catch (NullPointerException e) {
            System.out.println("of(null) kurulurken -> istisna: " + e.getClass().getSimpleName());
        }

        Optional<String> on = Optional.ofNullable(null);
        System.out.println("ofNullable(null) kurulurken: dustu degil, sonuc=" + on);
        try {
            on.get();
            System.out.println("ofNullable(null).get(): basarili");
        } catch (NoSuchElementException e) {
            System.out.println("ofNullable(null).get() -> istisna: " + e.getClass().getSimpleName());
        }
    }
}
```

```
of(null) kurulurken -> istisna: NullPointerException
ofNullable(null) kurulurken: dustu degil, sonuc=Optional.empty
ofNullable(null).get() -> istisna: NoSuchElementException
```

`Optional.of(null)` **kurulma anında** düşüyor: `of` çağrısı "bu değerin boş olmadığını
biliyorum" demektir, ve boş bir değerle çağrıldığında bu iddia derhal yanlışlanıyor.
`Optional.ofNullable(null)` ise düşmüyor — boş olabileceğini zaten kabul ediyor ve boş bir
kap (`Optional.empty()`) döndürüyor. Kırılma orada değil, kabın **içeriği okunduğunda**
oluyor: `get()` çağrısı `NoSuchElementException` ile düşüyor. İki yöntem de sonunda aynı
noktaya varabiliyor — boş bir kaptan `get()` çağırmak — ama `of` bunu hemen, kurulum
satırında yakalarken `ofNullable` bu riski kabın kendisiyle birlikte taşıyor ve denetimi
kabın kullanıldığı satıra erteliyor.

Bu erteleme bir kusur değil, tam olarak `Optional`'ın işidir: `ofNullable` çağrıldığı
yerde henüz kimse kabın nasıl kullanılacağını bilmiyor olabilir — belki hemen `map`
zincirine girecek, belki bir alana atanacak, belki hiç okunmayacak. `of` ise tersini
varsayar: burada, tam bu satırda, değerin dolu olduğunu iddia eder ve bu iddiayı
doğrulamak için beklemez. İkisi arasındaki fark, dersin başında ölçülen çağıranın
yükümlülüğü sorusuna küçük bir yanıt veriyor: yükümlülük hiçbir zaman ortadan kalkmıyor,
yalnızca **ne zaman** düşeceği çağıranın seçtiği yönteme göre değişiyor. Hangisinin
seçileceği çağıranın kararıdır: girdinin boş olamayacağından eminse `of` erken bir uyarı
sistemi kurar; girdinin boş olabileceğini zaten biliyorsa `ofNullable` bunu bir istisna
değil, sıradan bir durum olarak temsil eder.

## Özet

- Boş değer dönen bir imza ile `Optional` dönen bir imza, denetimsiz bırakılan çağrı
  sayısı bakımından eşittir: dört denetimsiz çağrının dördü de derlenir, biri çalışma
  zamanında düşer. `Optional` bu sayıyı azaltmaz.
- Kazanılan şey imzanın görünürlüğüdür: `Optional<T>` dönüş tipi yokluk olasılığını
  belgeye bakmadan, doğrudan imzada gösterir.
- `Optional` zincirlemeyi destekler: `map` boş bir kap üzerinde hiç çalışmaz, ara adımlarda
  ayrı bir boş değer denetimi yazmaya gerek kalmaz. Ama `orElse` argümanını kap dolu olsa
  bile her zaman hesaplar; `orElseGet` yalnızca kap boşken çalışır — ikisi aynı işi
  yapıyor görünse de bedelleri farklıdır.
- Sınırlayıcı ölçüm: `Optional` bir alan ya da parametre olarak kullanıldığında kazanç
  kaybolur — alanın kendisi boş olabilir ve bu, önlenmek istenen istisnayı bir kat dışarı
  taşır.
- `Optional.of(null)` kurulma anında `NullPointerException` ile düşer; `Optional.ofNullable(null)`
  düşmez, boş bir kap döndürür ve denetim `get()` çağrısına ertelenir. Seçim çağıranındır.

## Sonraki Adım

Bu payda tek bir tipin — fonksiyonel arayüzün, lambda'nın, akış zincirinin, toplayıcının,
`Optional`'ın — kütüphaneye ne söz verdiği ölçüldü, ve her seferinde söz derleyicinin
denetlediği kadar dardı: tek soyut yöntem tutulur, yerel yakalama dondurulur, sıra
çoğunlukla kaynaktan gelir, kabın tipi çoğunlukla belirsizdir. `Optional` de aynı dar
sözün son örneğidir — yokluk tiple söylendi, yükümlülük imzaya taşındı, ama denetim yine
çalışma zamanında kaldı. Sıradaki konu aynı soruyu artık dilin kendi tipleri üzerinde değil,
dış dünyayla kurulan sınırda sorar: baytın, dosyanın, saatin ve metnin standart kütüphaneye
ne söz verdiğini ölçer.
