Lambda adalah fungsi tanpa nama yang ditulis langsung di tempat ia dipakai, dan sejak Java 8 menjadi cara paling ringkas untuk mengisi satu metode abstrak milik interface fungsional. Topik ini membahas sintaks lambda dalam berbagai bentuk, hubungannya dengan tipe target, serta aturan penangkapan variabel dari sekitarnya yang dikenal sebagai aturan effectively final. Setelah membacanya, perilaku sekali pakai dapat ditulis dalam satu baris tanpa perlu membuat kelas tambahan.
Sintaks lambda dan tipe targetnya#
Bentuk paling umum lambda terdiri atas tiga bagian, yaitu daftar parameter, panah ->, dan isi yang berupa satu ekspresi atau blok pernyataan. Gambar berikut memetakan ketiganya pada contoh lambda yang membandingkan dua kata menurut panjangnya.
Tipe parameter tidak wajib ditulis karena kompiler menyimpulkannya dari tipe target, yaitu interface fungsional yang dilayani lambda tersebut. Pada penugasan Comparator<String> menurutPanjang = (a, b) -> ..., kompiler membaca tipe variabelnya, mengetahui bahwa compare(String, String) harus diisi, lalu menganggap a dan b bertipe String. Bila isinya satu ekspresi tunggal, nilai ekspresi itu otomatis menjadi nilai kembalian, sedangkan isi berbentuk blok memerlukan kurung kurawal dan return eksplisit bila menghasilkan nilai.
// tidak-dijalankan
Runnable tugas = () -> System.out.println("Cadangan data dimulai.");
Comparator<String> menurutPanjang = (a, b) -> Integer.compare(a.length(), b.length());
Function<String, Integer> panjang = teks -> {
System.out.println("Menghitung panjang");
return teks.length();
};Bentuk tanpa parameter memakai tanda kurung kosong, bentuk satu parameter boleh melepas tanda kurungnya, dan bentuk dua parameter atau lebih wajib memakai tanda kurung yang dipisah koma. Sebaliknya, tanda kurung tetap sah untuk satu parameter ketika tipenya ingin dinyatakan eksplisit, misalnya (String teks) -> teks.length(). Pilihan di antara bentuk-bentuk ini murni soal keringanan membaca, karena semuanya dikompilasi menjadi perilaku yang sama.
Menuliskan lambda untuk berbagai interface#
Lambda yang sama dapat melayani interface fungsional mana pun yang bentuk metodenya cocok. Program berikut memakai lambda untuk Runnable dari java.lang, interface PengubahTeks yang dideklarasikan sendiri, dan Predicate<String> dari java.util.function, sehingga ketiga wajah lambda pada bagian sebelumnya teruji sekaligus.
import java.util.function.Predicate;
public class SapaLambda {
interface PengubahTeks {
String ubah(String teks);
}
public static void main(String[] args) {
// tanpa parameter: tanda kurung kosong
Runnable tugas = () -> System.out.println("Cadangan data dimulai.");
tugas.run();
// satu parameter: tanda kurung boleh dilepas, tipe tidak perlu ditulis
PengubahTeks kapital = teks -> teks.toUpperCase();
System.out.println(kapital.ubah("jadwal piket"));
// isi berupa blok yang memuat beberapa pernyataan
Predicate<String> cukupPanjang = (teks) -> {
int batas = 5;
return teks.length() >= batas;
};
System.out.println("api : " + cukupPanjang.test("api"));
System.out.println("semangka : " + cukupPanjang.test("semangka"));
}
}Cadangan data dimulai.
JADWAL PIKET
api : false
semangka : trueMenjalankan programnya dari terminal cukup dengan satu perintah berikut.
# berada di folder tempat SapaLambda.java disimpan
java SapaLambda.javaCadangan data dimulai.
JADWAL PIKET
api : false
semangka : trueInterface PengubahTeks dideklarasikan di dalam berkas yang sama tanpa kata kunci public karena posisi kelas utama sudah dipegang SapaLambda. Metode ubah(String) memuat satu parameter dan satu nilai kembalian String, sehingga lambda teks -> teks.toUpperCase() cocok diisikan kepadanya. Pada cukupPanjang, isinya berupa blok yang menampung variabel lokal batas sebelum menjawab hasil pengujian, dan bentuk semacam ini berguna bila logikanya membutuhkan lebih dari satu langkah.
Coba demo berikut, pilih salah satu contohnya, lalu bandingkan bagian lambda yang menyingkat kerangka anonymous class.
Penangkapan variabel dan effectively final#
Lambda boleh memakai variabel yang dideklarasikan di luar tubuhnya, dan kebiasaan ini disebut penangkapan (capture). Syaratnya satu, yaitu variabel yang ditangkap harus effectively final, yang berarti nilainya tidak pernah berubah setelah diinisialisasi meskipun kata kunci final tidak ditulis. Aturan yang sama mengikat ke arah sebaliknya, yakni parameter lambda tidak boleh menimpa nama variabel lokal yang sudah ada di sekitarnya.
import java.util.List;
import java.util.function.IntPredicate;
public class UjiPenangkapan {
public static void main(String[] args) {
// ambang ditangkap lambda dan tidak pernah berubah, sehingga sah
int ambang = 60;
IntPredicate lolos = nilai -> nilai >= ambang;
System.out.println("70 lolos ambang " + ambang + " : " + lolos.test(70));
System.out.println("55 lolos ambang " + ambang + " : " + lolos.test(55));
// variabel penampung yang berubah dalam perulangan tidak boleh ditangkap
int hasil = 0;
for (int n : List.of(5, 10, 15)) {
hasil = hasil + n;
}
int total = hasil; // variabel baru yang tidak akan berubah lagi
Runnable lapor = () -> System.out.println("Total penjumlahan: " + total);
lapor.run();
}
}70 lolos ambang 60 : true
55 lolos ambang 60 : false
Total penjumlahan: 30Pada program di atas, ambang dan total keduanya effectively final. Variabel ambang diinisialisasi sekali dan tidak pernah ditugaskan ulang, sedangkan hasil yang berubah di dalam perulangan sengaja disalin ke variabel baru total sebelum ditangkap. Satu salinan itu sudah cukup karena lambda hanya menuntut kestabilan variabelnya, bukan kestabilan perhitungan yang menghasilkannya.
// tidak-dijalankan
int skor = 70;
Runnable lapor = () -> System.out.println("Skor: " + skor);
skor = 80; // kompiler menolak baris iniKompiler menolak program semacam itu dengan pesan local variables referenced from a lambda expression must be final or effectively final. Alasannya terletak pada masa hidup lambda yang bisa lebih panjang daripada frame metode pemiliknya, sehingga menyimpan salinan nilai saat penangkapan lebih aman daripada mengejar perubahan variabel yang bisa saja sudah tidak ada. Aturan ini hanya mengikat variabelnya, bukan isi objek yang ditunjuknya. Daftar yang di-add() dari dalam lambda tetap sah selama variabel penunjuknya tidak ditugaskan ulang. Kelonggaran semacam itu memang legal, tetapi menyembunyikan efek samping, sehingga sebaiknya dipakai hanya bila perilakunya memang dikehendaki.
Kesalahan umum#
- Menulis
returndan titik koma pada lambda ekspresi tunggal. Bentuk ekspresi sepertin -> n + 5sudah mengembalikan nilai tanpareturn, dan penambahanreturnatau titik koma justru membuatnya ditolak kompiler. Simpanreturnuntuk isi berbentuk blok. - Menangkap variabel yang masih berubah. Penugasan ulang setelah penangkapan membuat kompilasi gagal. Salin nilai ke variabel baru yang effectively final sebelum lambda ditulis.
- Mengira lambda memiliki tipe sendiri. Ekspresi lambda hanya bermakna melalui tipe targetnya, sehingga deklarasi seperti
var x = () -> 5ditolak karena konteksnya tidak menyebut interface fungsional apa pun. Beri tipe interface secara eksplisit pada variabelnya. - Memakai nama parameter yang menimpa variabel lokal. Lambda
teks -> ...di dalam metode yang sudah memiliki variabelteksmemicu error penimpaan nama. Ganti nama parameternya agar keduanya tidak bertabrakan.
Ringkasan#
- Lambda adalah fungsi tanpa nama dengan tiga bagian, yaitu daftar parameter, panah
->, dan isi yang berupa ekspresi tunggal atau blok pernyataan. - Tipe lambda disimpulkan dari tipe targetnya, yaitu interface fungsional tempat lambda diisikan, sehingga tipe parameter tidak perlu ditulis.
- Isi berbentuk blok memerlukan kurung kurawal dan
returneksplisit, sedangkan ekspresi tunggal otomatis menjadi nilai kembalian. - Variabel yang ditangkap lambda wajib effectively final, dan penugasan ulangnya menolak kompilasi, padahal isi objek yang ditunjuknya tetap boleh berubah.
Referensi#
- Lambda Expressions — Oracle — sintaks lambda, bentuk parameternya, dan aturan penangkapan variabel pada tutorial resmi Java.
- java.util.function (Java SE 21 & JDK 21) — Oracle — interface fungsional bawaan yang paling sering menjadi tipe target lambda.