Menu

Java Lambda İfadeleri: Özlü Fonksiyonel Kod

Java lambda ifadesinin ne olduğu, ok söz dizimi, fonksiyonel bir arayüzü nasıl uyguladığı, metot referansları ve değişken yakalama.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Etrafta Dolaştırabileceğin Davranış

Lambda ifadesi, tıpkı bir sayı ya da bir dizgiyi (string) yapacağın gibi bir metoda verebileceğin, bir değişkende saklayabileceğin ya da döndürebileceğin sıkışık bir kod bloğudur. Lambda'lardan önce davranış aktarmak, sadece tek bir metodu sarmalamak için bütün bir sınıf (ya da uzun soluklu bir anonim sınıf) yazmak demekti. Lambda bunu özüne indirger: parametreler ve gövde.

Bir lambda her zaman bir fonksiyonel arayüzü uygular; yani tam olarak bir soyut metoda sahip bir arayüzü (bunlarla arayüzler sayfasının sonunda tanışmıştın). Derleyici hangi arayüzü kastettiğini bağlamdan anlar ve lambda'nı o tek metotla eşleştirir.

x -> x * 2, apply metodunun eksiksiz uygulamasıdır. new yok, sınıf gövdesi yok, metot adı yok; bunların hepsini arayüz sağlar.

Ok Söz Dizimi

Her lambda'nın biçimi parametreler -> gövde şeklindedir. Parçalar, ne kadarına ihtiyacın olduğuna göre esner:

() -> 42                       // parametre yok
x -> x + 1                     // tek parametre, parantez isteğe bağlı
(x, y) -> x + y                // iki veya daha fazla parametre parantez gerektirir
(int x, int y) -> x + y        // türler isteğe bağlıdır - genellikle çıkarsanır
x -> {                          // blok gövde, süslü parantez ve return gerektirir
    int doubled = x * 2;
    return doubled + 1;
}

Tek bir ifadeden oluşan gövde (x -> x + 1) değerini örtük olarak döndürür; return anahtar sözcüğü gerekmez. Süslü parantez kullandığın an, normal bir blok yazıyorsundur ve metot bir şey döndürüyorsa açıkça return yazmalısın. Yaygın bir hata ikisini karıştırmaktır: x -> { x + 1 } derlenmez, çünkü bir blok bir deyim gerektirir (return x + 1;).

Lambda'lar Anonim Sınıfların Yerini Alır

Lambda'nın sana ne kazandırdığını görmenin en net yolu öncesi/sonrasıdır. Özel bir Comparator ile sıralamak eskiden şöyle görünürdü:

// öncesi - anonim sınıf
names.sort(new Comparator<String>() {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Aynı şey lambda olarak okunabilir tek bir satırdır:

Comparator bir fonksiyonel arayüzdür (tek soyut metodu compare'dir), bu yüzden lambda doğrudan yerine oturur. Tüm tören (new, sınıf, metot imzası) ortadan kalkar ve geriye yalnızca karşılaştırma mantığı kalır.

java.util.function Araç Kutusu

Kendi fonksiyonel arayüzünü tanımlamana nadiren ihtiyaç duyarsın. java.util.function paketi yaygın biçimleri içerir ve Java'nın kütüphane API'lerinin neredeyse tamamı bunları kabul eder:

  • Function<T, R> - bir T alır, bir R döndürür (apply)
  • Predicate<T> - bir T alır, bir boolean döndürür (test)
  • Supplier<T> - hiçbir şey almaz, bir T döndürür (get)
  • Consumer<T> - bir T alır, hiçbir şey döndürmez (accept)

Bunlar jenerik arayüzlerdir; Function<String, Integer>, tür güvenliğini korumak için önceki sayfada gördüğün jenerikleri yeniden kullanır. Kodunun almaya ve üretmeye ihtiyaç duyduğu şekle uyan biçimi seç.

Metot Referansları

Bir lambda var olan tek bir metodu çağırmaktan başka bir şey yapmadığında, onu :: kullanarak bir metot referansıyla değiştirebilirsin. Aynı değerdir, sadece daha doğrudan yazılmıştır:

Metot referansları çeşitli biçimlerde gelir: String::toUpperCase (her argüman üzerinde çağrılan bir örnek metodu), Math::max (bir statik metot), System.out::println (belirli bir nesne üzerindeki bir metot) ve ArrayList::new (bir yapıcı). Yalnızca açık biçimde okunduğunda birine başvur; hangi biçimin geçerli olduğunu düşünmek zorunda kalıyorsan sade bir lambda gayet iyidir.

Değişken Yakalama

Bir lambda, kendisini çevreleyen metottan yerel değişkenleri kullanabilir, ama yalnızca final ya da etkin biçimde final iseler; yani bir kez atanmış ve hiç değiştirilmemişlerse. Java değeri lambda oluşturulduğu anda yakalar, bu yüzden sonradan değişebilecek bir değişken belirsiz olurdu.

factor'ü herhangi bir yerde yeniden atarsan, derleyici lambda'yı "variable used in lambda expression should be final or effectively final" diyerek reddeder. Gerçekten paylaşılan değiştirilebilir bir duruma ihtiyacın olduğunda onun yerine bir nesne yakala (bir alan, bir dizi elemanı ya da bir AtomicInteger); çünkü içeriği değişse bile referans sabit kalır. Şunu da unutma: lambda'lar, anonim sınıfların aksine, kendi kapsamlarını oluşturmaz: bir lambda içindeki this, lambda'nın kendisine değil, onu çevreleyen örneğe gönderme yapar.

Sıradaki: Streams

Lambda'lar yapı taşıdır, varış noktası değil. Asıl getirileri Streams API'siyle gelir; burada filter, map ve reduce gibi (her biri bir lambda alan) işlemleri zincirleyerek veri dönüşümlerini, karmakarışık döngüler yerine okunabilir bir boru hattı (pipeline) olarak ifade edersin. Bir sonraki sayfa bu.

Sıkça Sorulan Sorular

Java'da lambda ifadesi nedir?

Lambda, fonksiyonel bir arayüzün (tek bir soyut metoda sahip arayüz) bir örneğini yazmanın kısa yoludur. Bütün bir anonim sınıf yerine parametreler -> gövde yazarsın. Derleyici lambda'yı arayüzün tek metoduyla eşleştirir, dolayısıyla Runnable r = () -> System.out.println("hi"); eksiksiz bir Runnable olur. Lambda'lar davranışı tıpkı veri gibi etrafta dolaştırmanı sağlar.

Java'da lambda ile metot referansı arasındaki fark nedir?

İkisi de fonksiyonel bir arayüzün örneğini üretir. Lambda'nın açık parametreleri ve bir gövdesi vardır (s -> s.toUpperCase()), metot referansı ise sadece var olan tek bir metodu çağıran bir lambda'nın kısaltmasıdır (String::toUpperCase). Lambda yalnızca argümanlarını isimli tek bir metoda iletmekten başka bir şey yapmıyorsa metot referansı kullan; daha kısadır ve daha iyi okunur.

Java lambda'sında kullanılan değişkenler neden final veya etkin biçimde final olmalı?

Lambda, kendini çevreleyen metottan yerel değişkenleri yakalayabilir, ama yalnızca atamadan sonra hiç değişmezlerse; "etkin biçimde final" olmanın anlamı budur. Java değeri yakalar, değişkene canlı bir referansı değil, bu yüzden yeniden atamaya izin vermek belirsiz ve iş parçacıkları arasında güvensiz olurdu. Paylaşılan değiştirilebilir bir duruma ihtiyacın varsa onun yerine bir alan ya da bir dizi/AtomicInteger sarmalayıcısı kullan.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA