İçindekiler
Java 8 ile hayatımıza giren lambda ifadeleri, davranışı veri gibi taşımanın kısa ve güçlü bir yoludur. Başka bir deyişle, yalnızca değerleri değil, çalıştırılacak işlemleri de metotlara gönderebiliriz. Bu yaklaşım; tekrar eden kodu azaltır, koleksiyon işlemlerini okunabilir hâle getirir ve Java’ya fonksiyonel programlama dünyasından kullanışlı araçlar kazandırır.
``
Lambda ifadesinin teorik temeli
Klasik nesne yönelimli programlamada davranışlar genellikle sınıfların metotları içinde bulunur. Fonksiyonel yaklaşımda ise bir işlem, başka bir işleme parametre olarak aktarılabilir veya sonuç olarak döndürülebilir. Matematikteki bir fonksiyonu şöyle gösterebiliriz:
\[f(x) = x \times 2\]Java’daki karşılığı (x) -> x * 2 biçiminde yazılabilir. Burada sol taraf parametreleri, -> operatörü bağlantıyı, sağ taraf ise çalıştırılacak gövdeyi temsil eder.
Genel lambda söz dizimi şöyledir:
(parametreler) -> ifade
Birden fazla komut varsa süslü parantez ve gerektiğinde return kullanılır:
(a, b) -> {
int toplam = a + b;
return toplam;
}
Lambda kendi başına havada duran isimsiz bir metot değildir. Java, lambdanın hangi parametreleri alacağını ve hangi türü döndüreceğini bir fonksiyonel arayüzün tek soyut metodundan öğrenir. Bu ilişkiye hedef tipleme denir.
Fonksiyonel arayüz nedir?
Yalnızca bir soyut metoda sahip arayüzler fonksiyonel arayüz olarak kullanılabilir. @FunctionalInterface zorunlu değildir; ancak yanlışlıkla ikinci bir soyut metot eklenmesini engellediği için önerilir.
@FunctionalInterface
interface Hesaplayici {
int hesapla(int a, int b);
}
Bu arayüze farklı davranışlar bağlayabiliriz:
Hesaplayici topla = (a, b) -> a + b;
Hesaplayici carp = (a, b) -> a * b;
System.out.println(topla.hesapla(4, 3)); // 7
System.out.println(carp.hesapla(4, 3)); // 12
Aynı arayüz iki farklı algoritmayı temsil eder. Böylece “hangi işlem yapılacak?” kararı, işlemi kullanan koddan ayrılır.
Anonim sınıf ile lambda karşılaştırması
Lambda ifadelerinden önce aynı davranış çoğunlukla anonim sınıfla yazılırdı:
Hesaplayici topla = new Hesaplayici() {
@Override
public int hesapla(int a, int b) {
return a + b;
}
};
| Özellik | Anonim sınıf | Lambda |
|---|---|---|
| Kod uzunluğu | Daha fazla | Daha kısa |
| Hedef | Sınıf veya arayüz | Fonksiyonel arayüz |
this anlamı |
Anonim nesneyi gösterir | Dış kapsamdaki nesneyi gösterir |
| Okunabilirlik | Basit işlemlerde düşük | Küçük davranışlarda yüksek |
Lambda her zaman daha iyi değildir. İşlem uzuyor, çok sayıda koşul içeriyor veya yeniden kullanılacak hâle geliyorsa isimlendirilmiş bir metot daha anlaşılır olabilir.
Hazır fonksiyonel arayüzler
Java, java.util.function paketinde sık kullanılan arayüzleri hazır sunar:
| Arayüz | Girdi | Çıktı | Amaç |
|---|---|---|---|
Predicate<T> |
T |
boolean |
Koşul sınamak |
Function<T, R> |
T |
R |
Dönüştürmek |
Consumer<T> |
T |
Yok | Değeri tüketmek |
Supplier<T> |
Yok | T |
Değer üretmek |
Predicate<Integer> ciftMi = sayi -> sayi % 2 == 0;
Function<String, Integer> uzunluk = metin -> metin.length();
Consumer<String> yazdir = metin -> System.out.println(metin);
Supplier<Double> rastgele = () -> Math.random();
Örneğin çiftlik kontrolü matematiksel olarak $n \bmod 2 = 0$ koşuluna dayanır. Predicate, bu koşulu yeniden kullanılabilir bir davranışa dönüştürür.
Stream API ile kullanım
Lambdaların en eğlenceli sahnesi Stream API’dir:
List<String> isimler = List.of("Ada", "Ahmet", "Ece", "Mehmet");
isimler.stream()
.filter(isim -> isim.length() >= 4)
.map(String::toUpperCase)
.forEach(System.out::println);
filter eleme, map dönüştürme, forEach ise sonuçları tüketme davranışını alır. String::toUpperCase bir metot referansıdır ve uygun lambda ifadesinin daha kısa gösterimidir.
Sonuç olarak lambda ifadeleri yalnızca sözdizimsel şeker değildir; davranışları arayüzler üzerinden aktararak gevşek bağlı, test edilebilir ve ifade gücü yüksek kod yazmamızı sağlar. Kısa işlemlerde lambda, karmaşık iş kurallarında ise anlamlı isimlere sahip metotlar kullanmak en dengeli yaklaşımdır.
Yorumlar