Menu

Lambda Expression Java: Kode Fungsional yang Ringkas

Apa itu lambda expression di Java, sintaks panahnya, cara lambda mengimplementasikan functional interface, method reference, dan menangkap variabel.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Perilaku yang Bisa Dioper

Lambda expression adalah blok kode ringkas yang bisa kamu berikan ke sebuah method, simpan di variabel, atau kembalikan, sama seperti angka atau string. Sebelum ada lambda, mengoper perilaku berarti menulis class utuh (atau anonymous class yang bertele-tele) hanya untuk membungkus satu method. Lambda memeras semua itu sampai ke intinya: parameter dan body-nya.

Lambda selalu mengimplementasikan functional interface, yaitu interface yang punya tepat satu abstract method (kamu sudah mengenalnya di akhir halaman interface). Compiler mengetahui interface mana yang kamu maksud dari konteksnya, lalu mencocokkan lambda-mu dengan satu method itu.

x -> x * 2 adalah seluruh implementasi apply. Tidak ada new, tidak ada body class, tidak ada nama method: semua itu sudah disediakan oleh interface-nya.

Sintaks Panah

Setiap lambda berbentuk parameters -> body. Bagian-bagiannya bisa menyesuaikan dengan kebutuhanmu:

() -> 42                       // no parameters
x -> x + 1                     // one parameter, parentheses optional
(x, y) -> x + y                // two or more parameters need parentheses
(int x, int y) -> x + y        // types are optional - usually inferred
x -> {                          // a block body needs braces and return
    int doubled = x * 2;
    return doubled + 1;
}

Body berupa satu ekspresi (x -> x + 1) secara implisit mengembalikan nilainya, tanpa kata kunci return. Begitu kamu memakai kurung kurawal, kamu sedang menulis blok biasa dan harus menulis return secara eksplisit kalau method-nya mengembalikan sesuatu. Kesalahan yang umum adalah mencampur keduanya: x -> { x + 1 } tidak bisa dikompilasi, karena blok butuh sebuah pernyataan (return x + 1;).

Lambda Menggantikan Anonymous Class

Cara paling jelas untuk melihat manfaat lambda adalah membandingkan sebelum dan sesudahnya. Dulu, mengurutkan dengan Comparator khusus terlihat seperti ini:

// before - anonymous class
names.sort(new Comparator<String>() {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Hal yang sama dalam bentuk lambda hanyalah satu baris yang mudah dibaca:

Comparator adalah functional interface (satu-satunya abstract method-nya adalah compare), jadi lambda bisa langsung dipasang. Semua formalitasnya, yaitu new, class, dan signature method, hilang, dan yang tersisa hanya logika perbandingannya.

Kotak Peralatan java.util.function

Kamu jarang perlu mendeklarasikan functional interface sendiri. Package java.util.function sudah menyediakan bentuk-bentuk yang umum, dan hampir semua API library Java menerimanya:

  • Function<T, R>: menerima T, mengembalikan R (apply)
  • Predicate<T>: menerima T, mengembalikan boolean (test)
  • Supplier<T>: tidak menerima apa-apa, mengembalikan T (get)
  • Consumer<T>: menerima T, tidak mengembalikan apa-apa (accept)

Semuanya interface generic: Function<String, Integer> memakai ulang generics yang kamu lihat di halaman sebelumnya agar tetap type-safe. Pilih yang bentuknya cocok dengan apa yang perlu diterima dan dihasilkan oleh kodemu.

Method Reference

Saat sebuah lambda tidak melakukan apa-apa selain memanggil satu method yang sudah ada, kamu bisa menggantinya dengan method reference memakai ::. Nilainya sama, hanya ditulis lebih langsung:

Method reference punya beberapa bentuk: String::toUpperCase (method instance yang dipanggil pada setiap argumen), Math::max (method static), System.out::println (method milik objek tertentu), dan ArrayList::new (constructor). Pakai method reference hanya kalau hasilnya jelas dibaca. Kalau kamu harus berpikir dulu bentuk mana yang berlaku, lambda biasa juga tidak masalah.

Menangkap Variabel

Lambda bisa memakai variabel lokal dari method di sekitarnya, tetapi hanya kalau variabel itu final atau effectively final, yaitu diberi nilai sekali dan tidak pernah diubah. Java menangkap nilainya pada saat lambda dibuat, jadi variabel yang nantinya bisa berubah akan menimbulkan ambiguitas.

Kalau kamu memberi nilai baru ke factor di mana pun, compiler menolak lambda itu dengan pesan "variable used in lambda expression should be final or effectively final." Saat kamu benar-benar butuh state bersama yang bisa berubah, tangkap sebuah objek, misalnya field, elemen array, atau AtomicInteger, karena referensinya tetap sama meskipun isinya berubah. Perhatikan bahwa lambda, berbeda dengan anonymous class, tidak membuat scope sendiri: this di dalam lambda merujuk ke instance yang membungkusnya, bukan ke lambda itu sendiri.

Berikutnya: Stream

Lambda adalah blok bangunannya, bukan tujuannya. Manfaat sebenarnya muncul bersama Streams API, tempat kamu merangkai operasi seperti filter, map, dan reduce, yang masing-masing menerima lambda, untuk menyatakan transformasi data sebagai pipeline yang mudah dibaca, bukan tumpukan loop yang kusut. Itu topik halaman berikutnya.

Pertanyaan yang sering diajukan

Apa itu lambda expression di Java?

Lambda adalah cara singkat untuk menulis instance dari functional interface, yaitu interface yang hanya punya satu abstract method. Daripada menulis anonymous class utuh, kamu cukup menulis parameters -> body. Compiler mencocokkan lambda itu dengan satu-satunya method di interface tersebut, jadi Runnable r = () -> System.out.println("hi"); sudah merupakan Runnable yang lengkap. Lambda memungkinkanmu mengoper perilaku ke sana kemari seperti data.

Apa perbedaan lambda dan method reference di Java?

Keduanya menghasilkan instance dari functional interface. Lambda punya parameter dan body yang eksplisit (s -> s.toUpperCase()), sedangkan method reference adalah singkatan untuk lambda yang hanya memanggil satu method yang sudah ada (String::toUpperCase). Pakai method reference saat lambda-nya tidak melakukan apa-apa selain meneruskan argumennya ke satu method bernama, karena lebih pendek dan lebih enak dibaca.

Kenapa variabel yang dipakai di lambda Java harus final atau effectively final?

Lambda bisa menangkap variabel lokal dari method yang membungkusnya, tetapi hanya kalau variabel itu tidak pernah berubah setelah diberi nilai. Itulah arti "effectively final". Java menangkap nilainya, bukan referensi hidup ke variabel itu, jadi membolehkan penugasan ulang akan membingungkan dan tidak aman di antara thread. Kalau kamu butuh state bersama yang bisa berubah, pakai field atau pembungkus array/AtomicInteger.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai