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

# Lambda İfadeleri

Bir lambda ifadesi çevresindeki bir yerel değişkeni yakaladığında o değişkenin etkin olarak final olması derleme zamanında zorunludur; bir alanı yakaladığında böyle bir kısıt yoktur ve nesne sonradan değişince aynı lambda başka bir sonuç verir. Yakalanan yerel değişkenin donan şey değeridir, gösterdiği nesne değil.

Önceki ders fonksiyonel arayüzün kendisine baktı: tek soyut yöntemin derleme zamanında
tutulduğunu, davranış sözünün hiçbir yerde tutulmadığını ölçtü. Bir lambda ifadesi yazıldığı
anda bu tipin yerinde bir şey daha olur — ifade, kendi gövdesinin dışındaki adlara başvurur
ve o adları **yakalar**. Yerel bir değişkenle bir alanın yakalanması aynı kısıta mı bağlıdır,
ve ikisi aynı sonucu mu üretir?

Bu ders bu soruyu doğrudan koşumla yanıtlar: aynı gövdeli, aynı işi yapan iki lambda — biri
bir yerel değişkeni, öteki bir alanı okuyarak — iki ayrı çağrı biçiminde kaç ayrı sonuç
üretiyor? Kapanış (closure) kavramının kendisi — bir işlevin kendi tanım ortamını
beraberinde taşıması — Programlama Temelleri ve JavaScript'te Nesneler ve Fonksiyonlar
kurslarında kuruldu ve burada tekrar açılmaz. Ölçülen şey kapanışın ne olduğu değil,
Java'nın onu hangi kısıtla sınırladığıdır.

## Lambda Bir Değişkeni Değeriyle Yakalar

- **FN8** — Aynı çarpma işlemi iki ayrı `IntUnaryOperator` ile yazılır: biri bir yerel
  değişkeni, öteki statik bir alanı okur. İkisi de kurulduğunda çarpan **2**'dir.
- **FN9** — Lambda kurulduktan sonra alan **5**'e, sonra **7**'ye değiştirilir; yerel
  değişkene ise hiç dokunulmaz — zaten dokunulamaz, aşağıdaki bölüm nedenini ölçer.
- **FN10** — Her iki lambda da aynı `applyAsInt(10)` çağrısıyla iki kez okunur; dönen
  değerler birebir karşılaştırılır.

```java
// Yakalama.java — degisken mi alan mi: lambda hangisini donduruyor
import java.util.function.IntUnaryOperator;

public class Yakalama {
    static int carpan = 2;

    public static void main(String[] args) {
        int carpanYerel = 2;
        IntUnaryOperator yerelYakalayan = x -> x * carpanYerel;
        System.out.println("yerel yakalama, ilk cagri   : " + yerelYakalayan.applyAsInt(10));
        carpan = 5;
        System.out.println("yerel yakalama, alan degisti: " + yerelYakalayan.applyAsInt(10));

        IntUnaryOperator alanYakalayan = x -> x * carpan;
        System.out.println("alan yakalama, carpan=5     : " + alanYakalayan.applyAsInt(10));
        carpan = 7;
        System.out.println("alan yakalama, carpan=7     : " + alanYakalayan.applyAsInt(10));
    }
}
```

```
yerel yakalama, ilk cagri   : 20
yerel yakalama, alan degisti: 20
alan yakalama, carpan=5     : 50
alan yakalama, carpan=7     : 70
```

`yerelYakalayan` iki çağrıda da **20** veriyor — aradaki satırda statik alan **5**'e
değişmiş olsa bile. Lambda kurulduğu anda `carpanYerel`'in o anki **değerini** almış ve o
değeri kendi içine gömmüş; dışarıda ne olursa olsun bu kopyayı okuyor. `alanYakalayan` ise
iki çağrıda iki ayrı sonuç veriyor: **50**, sonra **70**. Bu lambda bir değeri gömmedi,
`carpan` alanına giden bir **yolu** gömdü ve her çağrıda o yoldan güncel değeri okuyor.

Aynı gövde biçimi — `x -> x * bir_şey` — iki ayrı yakalama biçiminde iki ayrı sayıda
sonuç üretiyor: yerel değişkende **bir** sonuç, alanda **iki** ayrı sonuç. Sorunun yanıtı
burada net: yerel değişkenle alanın yakalanması aynı kısıta bağlı değildir ve ikisi aynı
sonucu üretmez.

## Etkin Olarak Final Kısıtı Derleme Zamanındadır

Yerel değişkenin neden sabit kaldığı bir önceki ölçümde görüldü; peki bu sabitlik
derleyicinin bir kararı mı, yoksa yalnızca bu örnekte mi böyle çıktı? Java'nın kuralı açıktır:
bir lambda ifadesi çevreleyen kapsamdaki bir yerel değişkeni yakalıyorsa, o değişken **etkin
olarak `final`** olmak zorundadır — yani ilk atamasından sonra bir daha hiç yeniden
atanmamalıdır, `final` sözcüğü yazılmasa bile.

- **FN11** — Aynı yöntem gövdesi iki biçimde denenir: yerel değişken yalnız bir kez atanıp
  lambda tarafından okunur; ikinci biçimde lambda kurulduktan sonra aynı değişkene bir kez
  daha atama yapılır.
- **FN12** — Derleyicinin ürettiği hata iletisinin metni basılmaz, yalnız derlenip
  derlenmediği bildirilir.

```java
// Derleme.java — ortak çekirdek: verilen kaynak metni derleniyor mu
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.file.*;
import java.util.spi.ToolProvider;

class Derleme {
    static boolean derlenirMi(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Path k = d.resolve(sinif + ".java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        return ToolProvider.findFirst("javac").orElseThrow()
                .run(bos, bos, "-d", d.toString(), k.toString()) == 0;
    }
}
```

```java
// EtkinFinal.java — yerel degiskeni yeniden atamak lambda'yi neden bozuyor
public class EtkinFinal {
    static final String TEMEL = """
        import java.util.function.IntSupplier;
        class Deneme {
            static IntSupplier yap() {
                int sayac = 0;
                IntSupplier s = () -> sayac;
                %s
                return s;
            }
        }
        """;

    public static void main(String[] args) throws Exception {
        System.out.println("degismeyen yerel            : "
                + (Derleme.derlenirMi(TEMEL.formatted(""), "Deneme") ? "derlendi" : "derlenmedi"));
        System.out.println("lambda sonrasi yeniden atama: "
                + (Derleme.derlenirMi(TEMEL.formatted("sayac = 1;"), "Deneme") ? "derlendi" : "derlenmedi"));
    }
}
```

```
degismeyen yerel            : derlendi
lambda sonrasi yeniden atama: derlenmedi
```

`sayac` değişkenine lambda kurulduktan **sonra** bir kez daha atama yapılan ikinci biçim
derlenmiyor — ve bu, çalışma zamanında değil derleme zamanında düşen bir hatadır. Kaynak
kodda `sayac = 1;` satırı lambda'nın çağrılmasından önce mi sonra mı çalışacağı önemli
değildir; derleyici denetim akışını yürütmez, yalnız değişkenin kapsamı içinde **birden
fazla atama** olup olmadığına bakar. Bir atama varsa değişken etkin olarak final'dir ve
yakalanabilir; ikinci bir atama görülürse — denetim akışında hiç ulaşılmayacak olsa bile --
yakalama derlenmez.

Bu, önceki dersle aynı örüntüdür: derleyici burada da bir **yapıya** bakıyor, bir davranışa
değil. Yerel değişkenin gerçekten değişip değişmediğini değil, kaynakta kaç kez atandığını
sayıyor. Alan için böyle bir sayım yoktur — `carpan` alanı istenildiği kadar yeniden
atanabilir ve derleyici hiç itiraz etmez, çünkü alan yakalaması yerel değişken yakalaması
gibi bir kopya çıkarmaz. Kısıtın gerekçesi de buradan çıkıyor: bir yerel değişken yöntem
döndükten sonra yığıttan silinir, ama lambda ondan sonra da yaşayabilir; derleyici bu
çelişkiyi değişkenin değerini kopyalayıp lambda'nın kendi içine gömerek çözer, ve kopyayı
tutarlı tutabilmek için o değişkenin tek atamalık kalmasını ister. Alan ise yığıtta değil
nesnenin ya da sınıfın kendisinde yaşar; lambda hangi nesneye ait olduğunu tutar, kopya
çıkarmaya gerek kalmaz.

## Klasik ve Gelişmiş Döngüde Aynı Ad İki Ayrı Değişkendir

Etkin olarak final kısıtının en görünür sonucu döngülerde çıkar. Gelişmiş `for` döngüsü
(`for (String s : liste)`) ile klasik `for` döngüsü (`for (int i = 0; ...; i++)`) aynı işi
yapıyor gibi görünür, ama bir lambda döngü değişkenini yakalamak istediğinde ikisi ayrışır.

- **FN13** — Üç ögelik bir listeden gelişmiş `for` ile geçilir; her yinelemede kurulan
  lambda, o yinelemenin değişkenini bir listeye ekler. Döngü bitince listedeki lambdaların
  hepsi çağrılır.
- **FN14** — Aynı deneme klasik `for` ile, artan bir sayaçla tekrarlanır; burada beklenen
  şey çalışma zamanı sonucu değil, derlenip derlenmediğidir.

```java
// Dongu.java — klasik ve gelismis for donguleri lambda yakalamasinda ayni mi
import java.util.*;
import java.util.function.Supplier;

public class Dongu {
    public static void main(String[] args) {
        List<String> kaynak = List.of("armut", "kiraz", "elma");
        List<Supplier<String>> yakalayanlar = new ArrayList<>();
        for (String s : kaynak) {
            yakalayanlar.add(() -> s);
        }
        System.out.print("gelismis for, uc lambdanin sonucu: ");
        for (Supplier<String> y : yakalayanlar) System.out.print(y.get() + " ");
        System.out.println();
    }
}
```

```
gelismis for, uc lambdanin sonucu: armut kiraz elma
```

Üç lambda üç ayrı değer veriyor: `armut`, `kiraz`, `elma`. Bu, "yakalama değeri dondurur"
kuralıyla ilk bakışta çelişir gibi görünür — döngü değişkeni `s` üç kez yeniden atanmadı
mı? Hayır: gelişmiş `for` her yinelemede `s` adında **yeni** bir yerel değişken kurar; o
tek atamalık değişken bir sonraki yinelemede başka bir değişkenle değiştirilir, aynı
değişken yeniden atanmaz. Üç lambda üç ayrı `s`'i yakaladı, bu yüzden üç ayrı değer
donduruldu.

```java
// DonguKlasik.java — klasik for'da sayac degiskeni yakalanabiliyor mu
public class DonguKlasik {
    static final String KAYNAK = """
        import java.util.*;
        import java.util.function.IntSupplier;
        class Deneme {
            static void yap() {
                List<IntSupplier> yakalayanlar = new ArrayList<>();
                for (int i = 0; i < 3; i++) {
                    yakalayanlar.add(() -> i);
                }
            }
        }
        """;

    public static void main(String[] args) throws Exception {
        System.out.println("klasik for, i yakalanabiliyor mu: "
                + (Derleme.derlenirMi(KAYNAK, "Deneme") ? "derlendi" : "derlenmedi"));
    }
}
```

```
klasik for, i yakalanabiliyor mu: derlenmedi
```

Klasik `for` döngüsünde durum tam tersidir. `i` tek bir değişkendir ve `i++` her
yinelemede onu yeniden atar; üç yineleme, aynı `i` üzerinde üç ayrı atama demektir. Bu,
etkin olarak final tanımını doğrudan bozar ve lambda `i`'yi yakalamaya çalıştığı satırda
derleme durur — daha önce ölçülen `sayac = 1;` örneğiyle birebir aynı kural, burada döngü
gövdesinin içinde. İki döngü sözdizimsel olarak benzer görünse de, gelişmiş `for` her turda
yeni bir ad kurarken klasik `for` tek bir adı tekrar tekrar atıyor — ve lambda yakalaması
tam olarak bu farkı ortaya çıkarıyor.

## Dört Sözdizimi, Aynı Yakalama Kuralı

Bir lambda ifadesinin gövdesi tek bir ifade olabilir, süslü parantezli bir blok olabilir,
parametre tipi açıkça yazılabilir ya da hiç yazılmayabilir — ve aynı işi yapan bir yöntem
zaten varsa, lambda hiç yazılmadan bir **yöntem referansı** (`Sınıf::yöntem`) ile de
belirtilebilir. Bu dört biçim ayrı ayrı sözdizimidir; hedef arayüz aynı olduğu sürece
dördü de aynı derleme yolundan geçer.

- **FN15** — Aynı `Function<Integer, Integer>` dört ayrı biçimde yazılır ve aynı girdiyle
  (`6`) çağrılır.

```java
// Sozdizim.java — dort ayri yazim, ayni sonuc
import java.util.function.Function;

public class Sozdizim {
    static int kare(int x) { return x * x; }

    public static void main(String[] args) {
        Function<Integer, Integer> ifade = x -> x * x;
        Function<Integer, Integer> govdeli = x -> { return x * x; };
        Function<Integer, Integer> tipYazili = (Integer x) -> x * x;
        Function<Integer, Integer> yontemReferansi = Sozdizim::kare;

        System.out.println("ifade biciminde     : " + ifade.apply(6));
        System.out.println("govde biciminde     : " + govdeli.apply(6));
        System.out.println("tip yazilarak       : " + tipYazili.apply(6));
        System.out.println("yontem referansiyla : " + yontemReferansi.apply(6));
    }
}
```

```
ifade biciminde     : 36
govde biciminde     : 36
tip yazilarak       : 36
yontem referansiyla : 36
```

Dört satır da aynı sonucu veriyor, çünkü dördü de aynı hedefe — `Function<Integer,
Integer>`'ın tek soyut yöntemine — bağlanıyor. `govdeli` biçimi `return` sözcüğünü zorunlu
kılar ve birden çok deyime izin verir; `ifade` biçimi tek bir değeri örtük olarak döndürür.
`tipYazili` parametre tipini açıkça yazar, `ifade` ve `govdeli` bu tipi hedef arayüzden
çıkarır. `yontemReferansi` ise hiç lambda yazmaz — zaten var olan `kare` yöntemine bir
isim üzerinden işaret eder.

Yöntem referansı yakalama kuralından muaf değildir, yalnız yakalayacağı bir şey olmayabilir.
Statik bir yönteme işaret eden `Sozdizim::kare` hiçbir şey yakalamaz, çünkü statik bir
yöntemin bağlı olduğu bir örnek yoktur. Ama bir **örnek** yöntemine, o örnek üzerinden
işaret eden bir referans — `liste::size` gibi — tıpkı `() -> liste.size()` lambda'sı gibi
`liste` değişkenini yakalar ve aynı kısıtlara, aynı sonuca tabidir; iki yazım aynı derleme
adımından geçer, yalnızca biri kısaltmadır. Sözdizim değişse de yakalama kuralı değişmiyor
-- kuralın koşulu lambda anahtar sözcüğü değil, çevreleyen kapsamdan bir adın okunmasıdır.

## Donan Değer, Donmayan Nesne

Buraya kadarki ölçüm bir yanılgıya açıktır: "yerel değişken sabittir" cümlesi, yakalanan
nesnenin de sabit kaldığı izlenimini verebilir. Sınırlayıcı ölçüm bunu ayırıyor.

- **FN16** — Bir liste değişkeni bir kez atanıyor ve hiç yeniden atanmıyor — etkin olarak
  final kuralını sağlıyor. Lambda listenin **boyutunu** okuyor, listenin kendisini değil.

```java
// Referans.java — yerel degisken degeri donduruyor, nesneyi dondurmuyor
import java.util.*;
import java.util.function.Supplier;

public class Referans {
    public static void main(String[] args) {
        List<String> liste = new ArrayList<>(List.of("alfa"));
        Supplier<Integer> boyutOku = () -> liste.size();
        System.out.println("lambda kurulunca boyut : " + boyutOku.get());
        liste.add("beta");
        liste.add("gama");
        System.out.println("liste degisince boyut  : " + boyutOku.get());
    }
}
```

```
lambda kurulunca boyut : 1
liste degisince boyut  : 3
```

`liste` değişkeni bir kez atandı ve bir daha hiç yeniden atanmadı; etkin olarak final
kısıtı baştan sona sağlandı. Buna rağmen `boyutOku.get()` iki çağrıda iki ayrı sayı
veriyor: **1**, sonra **3**. Çelişki görünüşte; kısıtın dondurduğu şey `liste`
değişkeninin **kendisi** — yani hangi nesneyi gösterdiği — oldu, o nesnenin **içeriği**
değil. `liste` hâlâ aynı `ArrayList` nesnesini gösteriyor, ama o nesnenin içine iki öge daha
eklendi ve lambda her çağrıda güncel içeriği okuyor.

Bu, bir önceki bölümün "yerel yakalama sabit kalır" gözlemini sınırlıyor: sabit kalan
referanstır, referansın gösterdiği değişebilir nesne değil. Etkin olarak final kuralı bir
değişmezlik (immutability) garantisi vermez; yalnızca bir **adın** akış boyunca aynı nesneyi
göstermesini zorunlu kılar. `İlkel Tipler ve Sarmalayıcılar` dersinde ölçülen değiştirilemez
dizgi ile burada yakalanan değişebilir liste bu yüzden ayrı davranıyor — ilkinde nesnenin
kendisi hiç değişemez, ikincisinde nesne yerinde değişebiliyor ve yakalama bunu engellemiyor.

## Özet

- Bir lambda ifadesi yerel bir değişkeni yakaladığında o değişkenin **değerini** kopyalayıp
  içine gömer; alan yakaladığında bir kopya çıkarmaz, alana giden yolu tutar.
- Aynı gövdeli iki lambda, iki yakalama biçiminde iki ayrı sonuç sayısı üretir: yerel
  yakalamada değişmeyen tek bir sonuç, alan yakalamada alan değiştikçe değişen ayrı sonuçlar.
- Yakalanan yerel değişkenin **etkin olarak `final`** olması derleme zamanında zorunludur;
  lambda kurulduktan sonra bile olsa aynı değişkene ikinci bir atama yapılması derlemeyi
  durdurur.
- Alan yakalamada böyle bir kısıt yoktur; alan yakalama tarihinden sonra da yeniden
  atanabilir ve derleyici buna karışmaz.
- Sınırlayıcı ölçüm: etkin olarak final kısıtı değişkenin gösterdiği nesneyi değil, yalnız
  değişkenin kendisini dondurur. Yakalanan referansın gösterdiği liste sonradan değişirse
  lambda değişmiş içeriği görür.
- Lambda ifadesinin dört sözdizimi (ifade, gövde, açık tip, yöntem referansı) aynı hedefe
  bağlanır ve aynı sonucu verir; yöntem referansı yakalama kuralından muaf değildir, yalnız
  yakalayacağı bir şey olmayabilir.

## Sonraki Adım

Bu ders tek bir lambda ifadesinin çevresinden ne aldığını ve dört sözdiziminin aynı hedefe
nasıl bağlandığını ölçtü. Standart kütüphanenin asıl kullanım biçimi böyle tek bir lambda
değil, birbirine art arda bağlanmış birkaç lambdadır — bir akış zinciri. Her halka kendi
başına doğru çalışsa da, zincire dizildiğinde ayrı bir soru doğar: zincirin hangi parçası
kaynağın sırasına bağlıdır, hangisi her koşulda aynı sırayı verir, hangisi bir sonlandırıcı
olmadan hiç çalışmaz ve hangi kural bütünüyle çağırana kalır. Sıradaki ders bu soruyu bir
akış zincirinin on ayrı işlemi üzerinden koşturarak yanıtlar.
