Eğitim Portalı/Java/Lambda İfadeleri ve Functional Interface'ler
Java01-java/11-lambda-ve-functional-interface

Lambda İfadeleri ve Functional Interface'ler

Java 8, dile köklü bir yenilik getirdi: davranışı (kodu) bir değer gibi taşıyabilmek. Lambda ifadeleri sayesinde bir metodu, bir değişkene atayıp parametre olarak geçebiliriz. Bu, koleksiyonları işlemekten olay dinley…

Lambda İfadeleri ve Functional Interface'ler

Java 8, dile köklü bir yenilik getirdi: davranışı (kodu) bir değer gibi taşıyabilmek. Lambda ifadeleri sayesinde bir metodu, bir değişkene atayıp parametre olarak geçebiliriz. Bu, koleksiyonları işlemekten olay dinleyicilerine kadar her yerde kodu kısaltır ve okunabilir kılar. Lambda'ları anlamak, bir sonraki konu olan Stream API'nin de kapısını açar.

Functional interface nedir?

Lambda'nın arkasındaki kavram functional interface'tir: yalnızca tek bir soyut metodu olan interface. Bir lambda, işte bu tek metodun gövdesini sağlar. @FunctionalInterface anotasyonu, derleyicinin "gerçekten tek soyut metot var mı?" diye kontrol etmesini sağlar:

@FunctionalInterface
interface Hesap {
    int uygula(int a, int b);
}

Lambda sözdizimi

Eskiden bir interface'i kısaca uygulamak için anonim iç sınıf yazardık; lambda aynı işi çok daha az gürültüyle yapar:

// Anonim sınıf
Hesap topla = new Hesap() {
    public int uygula(int a, int b) { return a + b; }
};
// Lambda — aynısı
Hesap topla = (a, b) -> a + b;

Lambda'nın anatomisi: (parametreler) -> gövde. Gövde tek ifadeyse süslü parantez ve return gerekmez; çok satırlıysa { ... return ...; } kullanırsın. Örnek 1 (./Ornek1.java) anonim sınıftan lambda'ya geçişi ve lambda'yı argüman olarak geçmeyi gösterir.

Hazır functional interface'ler

java.util.function paketi, en sık ihtiyaç duyacağın functional interface'leri hazır sunar; böylece her seferinde kendin tanımlamazsın:

InterfaceMetotAnlamı
Predicate<T>boolean test(T)Bir koşulu sınar
Function<T,R>R apply(T)T'yi R'ye dönüştürür
Consumer<T>void accept(T)T'yi alır, yan etki yapar
Supplier<T>T get()Girdisiz değer üretir
BiFunction<T,U,R>R apply(T,U)İki girdi, bir çıktı
UnaryOperator<T>T apply(T)T → T

Bunların çoğu birleştirilebilir: predicate.and(...), function.andThen(...) gibi. Örnek 2 (./Ornek2.java) bu beş tipi tek tek çalıştırır ve birleştirme metotlarını gösterir.

Method reference

Lambda yalnızca var olan bir metodu çağırıyorsa, onu daha da kısaltabilirsin: method reference. s -> System.out.println(s) yerine System.out::println yazarsın:

liste.forEach(System.out::println);
Comparator.comparingInt(Kisi::yas);

Dört biçimi vardır: Tip::statikMetot, nesne::metot, Tip::örnekMetot, Tip::new (constructor). Okunabilirliği bozmadığı sürece method reference daha temizdir.

Pratik: Comparator ile sıralama

Lambda ve method reference'ın en sık kullanıldığı yerlerden biri sıralamadır. Comparator.comparing(...) bir alana göre karşılaştırıcı üretir; thenComparing(...) ile eşitlik durumunda ikinci ölçüt eklersin, reversed() ile sırayı tersine çevirirsin:

kisiler.sort(Comparator.comparingInt(Kisi::yas).thenComparing(Kisi::ad));

Örnek 3 (./Ornek3.java) kişileri yaşa, sonra ada; ardından şehre ve azalan yaşa göre sıralayarak çok ölçütlü karşılaştırıcıları gösterir.

Özet

Lambda'ların davranışı bir değer gibi taşımamızı sağladığını, functional interface'lerin bunun temeli olduğunu öğrendik. java.util.function paketindeki hazır tipleri, method reference'ı ve Comparator ile pratik sıralamayı gördük. Bu temel, hemen sıradaki konuda — Stream API'de — verileri akış halinde işlerken doğrudan işine yarayacak.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Functional interface ve lambda — anonim sınıftan lambda'ya.
2// Çalıştırma: java Ornek1.java
3public class Ornek1 {
4
5    // Functional interface: TEK soyut metodu olan interface.
6    // @FunctionalInterface bunu derleyiciye doğrulattırır.
7    @FunctionalInterface
8    interface Hesap {
9        int uygula(int a, int b);
10    }
11
12    static int islemYap(int a, int b, Hesap h) {
13        return h.uygula(a, b);
14    }
15
16    public static void main(String[] args) {
17        // ESKİ yol: anonim iç sınıf (uzun ve gürültülü).
18        Hesap toplaAnonim = new Hesap() {
19            @Override
20            public int uygula(int a, int b) {
21                return a + b;
22            }
23        };
24
25        // YENİ yol: lambda (aynı şey, çok daha kısa).
26        Hesap topla = (a, b) -> a + b;
27        Hesap carp  = (a, b) -> a * b;
28        Hesap fark  = (a, b) -> Math.abs(a - b);
29
30        System.out.println("anonim toplama : " + islemYap(7, 3, toplaAnonim));
31        System.out.println("lambda toplama : " + islemYap(7, 3, topla));
32        System.out.println("lambda çarpma  : " + islemYap(7, 3, carp));
33        System.out.println("lambda fark    : " + islemYap(7, 3, fark));
34
35        // Lambda'yı doğrudan argüman olarak da geçebiliriz:
36        System.out.println("doğrudan lambda: " + islemYap(10, 4, (a, b) -> a / b));
37    }
38}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Hazır functional interface'ler — Predicate, Function, Consumer, Supplier.
2// Çalıştırma: java Ornek2.java
3import java.util.function.BiFunction;
4import java.util.function.Consumer;
5import java.util.function.Function;
6import java.util.function.Predicate;
7import java.util.function.Supplier;
8
9public class Ornek2 {
10
11    public static void main(String[] args) {
12        // --- Predicate<T>: bir koşulu test eder, boolean döndürür ---
13        Predicate<Integer> ciftMi = n -> n % 2 == 0;
14        System.out.println("ciftMi(4) = " + ciftMi.test(4));
15        System.out.println("ciftMi(7) = " + ciftMi.test(7));
16        // Predicate'ler birleştirilebilir:
17        Predicate<Integer> pozitifVeCift = ciftMi.and(n -> n > 0);
18        System.out.println("pozitifVeCift(-4) = " + pozitifVeCift.test(-4));
19
20        // --- Function<T,R>: T alır, R döndürür ---
21        Function<String, Integer> uzunluk = s -> s.length();
22        System.out.println("\nuzunluk(\"merhaba\") = " + uzunluk.apply("merhaba"));
23        // andThen ile zincirleme: önce uzunluk, sonra kare
24        Function<String, Integer> uzunlukKare = uzunluk.andThen(n -> n * n);
25        System.out.println("uzunlukKare(\"abc\") = " + uzunlukKare.apply("abc"));
26
27        // --- Consumer<T>: T alır, bir şey döndürmez (yan etki yapar) ---
28        Consumer<String> yazdir = s -> System.out.println("  -> " + s);
29        System.out.println("\nConsumer:");
30        yazdir.accept("merhaba");
31        yazdir.accept("dünya");
32
33        // --- Supplier<T>: girdi almaz, bir değer üretir ---
34        Supplier<String> selamUret = () -> "Günaydın!";
35        System.out.println("\nSupplier: " + selamUret.get());
36
37        // --- BiFunction<T,U,R>: iki girdi, bir çıktı ---
38        BiFunction<Integer, Integer, Integer> topla = (a, b) -> a + b;
39        System.out.println("BiFunction topla(3,5) = " + topla.apply(3, 5));
40    }
41}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Method reference ve Comparator ile nesne sıralama.
2// Çalıştırma: java Ornek3.java
3import java.util.ArrayList;
4import java.util.Comparator;
5import java.util.List;
6
7public class Ornek3 {
8
9    record Kisi(String ad, int yas, String sehir) {}
10
11    public static void main(String[] args) {
12        List<Kisi> kisiler = new ArrayList<>(List.of(
13                new Kisi("Ada", 30, "İzmir"),
14                new Kisi("Burak", 25, "Ankara"),
15                new Kisi("Ceren", 30, "Ankara"),
16                new Kisi("Deniz", 25, "İzmir")
17        ));
18
19        // Method reference: lambda'nın daha da kısa hali.
20        // s -> System.out.println(s)  yerine  System.out::println
21        System.out.println("Method reference ile yazdırma:");
22        kisiler.forEach(k -> System.out.println("  " + k));
23
24        // Comparator.comparing + method reference (Kisi::yas)
25        kisiler.sort(Comparator.comparingInt(Kisi::yas));
26        System.out.println("\nYaşa göre:");
27        kisiler.forEach(k -> System.out.println("  " + k.ad() + " (" + k.yas() + ")"));
28
29        // Çok ölçütlü sıralama: önce yaş, eşitse ada göre (thenComparing)
30        kisiler.sort(Comparator.comparingInt(Kisi::yas).thenComparing(Kisi::ad));
31        System.out.println("\nYaş, sonra ad:");
32        kisiler.forEach(k -> System.out.println("  " + k.ad() + " (" + k.yas() + ")"));
33
34        // Tersten sıralama (reversed) ve şehre göre gruplı görünüm
35        kisiler.sort(Comparator.comparing(Kisi::sehir).thenComparing(Comparator.comparingInt(Kisi::yas).reversed()));
36        System.out.println("\nŞehir, sonra yaş (azalan):");
37        kisiler.forEach(k -> System.out.println("  " + k.sehir() + " - " + k.ad() + " (" + k.yas() + ")"));
38    }
39}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.