Anonymous class pernah menjadi satu-satunya cara Java mengirim behavior ke dalam method — dan lima baris ritual (new Comparator<String>() { ... }) demi satu baris logika sungguhan. Java 8 menggantikan ritual itu dengan lambda expression: sintaks (parameter) -> tubuh yang langsung mengimplementasikan method abstrak tunggal dari functional interface. Kita sudah mengenal rumahnya pada Chapter sebelumnya; sekarang kita belajar semua bentuk penghuninya, dari yang tanpa parameter sampai blok multi-baris, lalu membandingkannya langsung dengan anonymous class.
Empat Bentuk Dasar Lambda #
Bentuk lambda mengikuti kebutuhan method abstraknya: parameter bisa tidak ada, bisa satu, bisa banyak; tubuh bisa berupa satu ekspresi atau blok penuh dengan return. Sintaks intinya selalu sama — daftar parameter, tanda panah ->, lalu tubuh. Compiler javac menyimpulkan tipe parameter dari functional interface yang menjadi target type, sehingga kita nyaris tidak pernah menulis tipe parameter secara eksplisit.
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class BentukLambda {
public static void main(String[] args) {
// Bentuk 1: tanpa parameter, kurung wajib dan kosong
Supplier<String> sapaan = () -> "Selamat datang!";
System.out.println(sapaan.get());
// Bentuk 2: satu parameter, kurung boleh dibuang
Function<String, Integer> panjang = teks -> teks.length();
System.out.println(panjang.apply("fungsional"));
// Bentuk 3: dua parameter, kurung dan koma wajib
BiFunction<Integer, Integer, Integer> jumlah = (a, b) -> a + b;
System.out.println(jumlah.apply(7, 5));
// Bentuk 4: blok statement, return eksplisit dan titik koma
Function<Integer, String> status = nilai -> {
if (nilai >= 70) {
return "LULUS";
}
return "REMEDIAL";
};
System.out.println(status.apply(85));
System.out.println(status.apply(60));
}
}Output yang Dihasilkan:
Selamat datang!
10
12
LULUS
REMEDIALMari kita bedah keempat bentuk pada program di atas:
- Baris 9 (
() -> "Selamat datang!"): method abstrakSupplier.get()tidak menerima parameter, jadi kurung harus ditulis dan dibiarkan kosong. Tubuh berupa ekspresi"Selamat datang!"— nilai ekspresi menjadi nilai kembalian tanpa kata kuncireturn. - Baris 13 (
teks -> teks.length()): dengan tepat satu parameter, kurung boleh dibuang sehinggateksterbaca seperti variabel biasa. Compiler tahuteksbertipeStringkarena target type-nyaFunction<String, Integer>— methodapplymenerimaString. - Baris 17 (
(a, b) -> a + b): dua parameter wajib berkurung dan dipisah koma. Tipeadanbdisimpulkan sebagaiIntegerdariBiFunction<Integer, Integer, Integer>. - Baris 21-28 (
nilai -> { ... return ...; }): bila tubuh butuh percabangan, kita pakai blok dengan kurung kurawal. Di dalam blok, aturan method biasa berlaku —returnditulis eksplisit dan setiap statement diakhiri titik koma. - Baris 29-30:
status.apply(85)danstatus.apply(60)menjalankan blok yang sama dengan masukan berbeda — lambda adalah nilai yang bisa dipanggil berulang kali seperti method.
Menggantikan Anonymous Class #
Sebelum ada lambda, Comparator dan Runnable diimplementasikan dengan anonymous class: deklarasi tipe, kata kunci new, peng-override, dan tubuh — padahal yang penting hanya tubuhnya. Program berikut menjalankan tugas yang sama dua kali, pertama dengan anonymous class, lalu dengan lambda:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class AnonimVsLambda {
public static void main(String[] args) {
List<String> nama = new ArrayList<>(List.of("Kurniawan", "Budi", "Siti"));
// Versi 1: anonymous class (gaya sebelum Java 8)
Comparator<String> panjangAsc = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
nama.sort(panjangAsc);
System.out.println("Anonymous class : " + nama);
// Versi 2: lambda dengan makna yang sama persis
nama.sort((a, b) -> Integer.compare(b.length(), a.length()));
System.out.println("Lambda : " + nama);
// Runnable: anonymous class vs lambda
Runnable tugasAnonim = new Runnable() {
@Override
public void run() {
System.out.println("Tugas berjalan (anonymous)");
}
};
tugasAnonim.run();
Runnable tugasLambda = () -> System.out.println("Tugas berjalan (lambda)");
tugasLambda.run();
}
}Output yang Dihasilkan:
Anonymous class : [Budi, Siti, Kurniawan]
Lambda : [Kurniawan, Budi, Siti]
Tugas berjalan (anonymous)
Tugas berjalan (lambda)Perhatikan tiga hal dari hasil eksekusi tersebut:
- Baris 11-18 (anonymous class): lima baris hanya untuk mengatakan “bandingkan panjang string”. Kelas tanpa nama dibuat saat runtime sebagai turunan
Comparator<String>— objek sungguhan dengan alokasi memori dan methodcompareyang di-override. - Baris 25 (lambda):
(a, b) -> Integer.compare(b.length(), a.length())menyatakan hal yang sama dalam satu baris. JVM tidak membuat class turunan baru per penulisan lambda; implementasi internalnya berbentuk invokedynamic — mekanisme yang menghasilkan objek fungsional secara efisien pada pemanggilan pertama. - Baris 28-39 (Runnable): pola yang sama berlaku untuk
Runnable. Kedua versi menghasilkan keluaran identik karena keduanya mengimplementasikan method abstrak yang sama; yang berubah hanya jumlah huruf yang kita ketik dan tingkat kebisingan visual saat membaca kode.
Perbandingan ini menjelaskan mengapa lambda cepat diadopsi: bukan karena kemampuan baru (anonymous class tetap bisa melakukannya), melainkan karena biaya penulisan dan pembacaan turun drastis. Aturan praktisnya sederhana — jika sebuah anonymous class hanya berisi satu method dan tubuhnya pendek, ganti dengan lambda; jika anonymous class menyimpan state (variabel anggota) atau meng-override lebih dari satu method, class biasa tetap pilihan yang tepat.
Inferensi Tipe dan Var #
Lambda tidak pernah berdiri tanpa konteks: compiler selalu memerlukan target type sebelum bisa menyimpulkan tipe parameter. Karena itu pernyataan var banding = (a, b) -> ...; tanpa informasi lebih ditolak compiler — var tidak membawa tipe interface, sehingga compiler tidak punya method abstrak untuk dipetakan. Sebaliknya, kombinasi Comparator<String> banding = (a, b) -> ... atau var banding = Comparator.<String>comparingInt(String::length); berjalan baik karena tipe target hadir secara eksplisit.
Kita sekarang bisa menulis behavior dalam bentuk paling ringkas. Chapter berikutnya mempersingkat lagi: saat tubuh lambda hanya memanggil satu method yang sudah ada, method reference seperti String::length menggantikan seluruh lambda tanpa kehilangan makna.
Referensi #
- dev.java: Lambda Expressions
- Oracle: java.util.function (Package Summary)
- Java Language Specification (JLS) — Bab 15
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AnonimVsLambda.java
Unduh berkasimport java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class AnonimVsLambda {
public static void main(String[] args) {
List<String> nama = new ArrayList<>(List.of("Kurniawan", "Budi", "Siti"));
// Versi 1: anonymous class (gaya sebelum Java 8)
Comparator<String> panjangAsc = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
nama.sort(panjangAsc);
System.out.println("Anonymous class : " + nama);
// Versi 2: lambda dengan makna yang sama persis
nama.sort((a, b) -> Integer.compare(b.length(), a.length()));
System.out.println("Lambda : " + nama);
// Runnable: anonymous class vs lambda
Runnable tugasAnonim = new Runnable() {
@Override
public void run() {
System.out.println("Tugas berjalan (anonymous)");
}
};
tugasAnonim.run();
Runnable tugasLambda = () -> System.out.println("Tugas berjalan (lambda)");
tugasLambda.run();
}
}
BentukLambda.java
Unduh berkasimport java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class BentukLambda {
public static void main(String[] args) {
// Bentuk 1: tanpa parameter, kurung wajib dan kosong
Supplier<String> sapaan = () -> "Selamat datang!";
System.out.println(sapaan.get());
// Bentuk 2: satu parameter, kurung boleh dibuang
Function<String, Integer> panjang = teks -> teks.length();
System.out.println(panjang.apply("fungsional"));
// Bentuk 3: dua parameter, kurung dan koma wajib
BiFunction<Integer, Integer, Integer> jumlah = (a, b) -> a + b;
System.out.println(jumlah.apply(7, 5));
// Bentuk 4: blok statement, return eksplisit dan titik koma
Function<Integer, String> status = nilai -> {
if (nilai >= 70) {
return "LULUS";
}
return "REMEDIAL";
};
System.out.println(status.apply(85));
System.out.println(status.apply(60));
}
}