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:
| Interface | Metot | Anlamı |
|---|---|---|
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ılabilir1// 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}Ornek2
çalıştırılabilir1// 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}Ornek3
çalıştırılabilir1// 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}