Menguasai Java Lambda Expressions: Dari Dasar hingga Stream API
Mastering Java Lambda Expressions
Jelajahi ekspresi lambda Java, antarmuka fungsional, generics, dan Stream API dengan contoh yang jelas.
Java 8 memperkenalkan ekspresi lambda untuk meningkatkan daya ekspresif bahasa, memungkinkan pengembang menulis kode yang lebih ringkas dan mudah dibaca.
Sebelum memulai lambda, penting untuk memahami antarmuka fungsional, tulang punggung implementasi lambda.
Apa itu Antarmuka Fungsional?
Antarmuka fungsional adalah antarmuka Java yang berisi tepat satu metode abstract. Metode tunggal ini mendefinisikan tujuan antarmuka.
Antarmuka Runnable dari java.lang adalah contoh klasik, yang hanya mengekspos metode run().
Contoh 1: Mendefinisikan Antarmuka Fungsional
import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface { // Metode abstract tunggal double getValue(); }
Di sini, MyInterface diannotasi dengan @FunctionalInterface, yang memaksa compiler untuk menekati kontrak antarmuka fungsional, mencegah penambahan secara tidak sengaja beberapa metode abstract. Annotasi ini opsional tetapi sangat direkomendasikan untuk kejelasan dan keamanan.
Di Java 7, antarmuka fungsional disebut Single Abstract Methods (SAMs) dan biasanya diimplementasikan menggunakan anonymous class.
Contoh 2: Implementasi SAM dengan Anonymous Class
public class FunctionInterfaceTest { public static void main(String[] args) { // Anonymous class implementing Runnable new Thread(new Runnable() { @Override public void run() { System.out.println("I just implemented the Runnable Functional Interface."); } }).start(); } }
Output:
I just implemented the Runnable Functional Interface.
Meskipun anonymous class mengurangi boilerplate dibandingkan dengan implementasi tradisional, sintaksnya masih bertele-tele. Ekspresi lambda menghilangkan kebutuhan untuk nama metode eksplisit saat meneruskan antarmuka fungsional sebagai argumen.
Pengenalan Ekspresi Lambda
Ekspresi lambda esensialmente adalah metode anonim yang menyediakan implementasi ringkas dari metode abstract antarmuka fungsional. Ia tidak dieksekusi secara mandiri; sebagai gantinya, ia menyediakan perilaku untuk metode antarmuka.
Cara Mendefinisikan Ekspresi Lambda di Java
Sintaks umum adalah:
(parameter list) -> lambda body
Operator panah (->) memisahkan daftar parameter dari body. Berikut adalah contoh konkret.
Asumsikan kita memiliki metode yang mengembalikan Pi:
double getPiValue() { return 3.1415; }
Menggunakan lambda:
() -> 3.1415
Tanpa parameter, sisi kiri panah kosong, dan sisi kanan adalah body ekspresi yang mengembalikan nilai.
Jenis Body Lambda
Body lambda Java ada dalam dua bentuk:
1. Expression Body – berisi satu ekspresi tunggal.
() -> System.out.println("Lambdas are great");
2. Block Body – dikapsulasi dalam kurawal, memungkinkan beberapa pernyataan dan return opsional.
() -> { double pi = 3.1415; return pi; };
Catatan: Body ekspresi tidak memerlukan return eksplisit, sedangkan body blok memerlukannya jika nilai dikembalikan.
Contoh 3: Ekspresi Lambda dalam Praktik
Mari kita tulis program Java yang mengembalikan Pi menggunakan ekspresi lambda.
import java.lang.FunctionalInterface; @FunctionalInterface interface MyInterface { double getPiValue(); } public class Main { public static void main(String[] args) { MyInterface ref = () -> 3.1415; System.out.println("Value of Pi = " + ref.getPiValue()); } }
Output:
Value of Pi = 3.1415
Kesimpulan utama:
MyInterface mendefinisikan satu metode abstract. - Referensi ke
MyInterface dideklarasikan tetapi tidak dibuat instance secara langsung. - Ekspresi lambda menyediakan implementasi untuk
getPiValue(). - Memanggil
ref.getPiValue() memicu body lambda.
Ekspresi Lambda dengan Parameter
Seperti metode, lambda dapat menerima parameter. Contoh:
(n) -> (n % 2) == 0
Lambda ini memeriksa apakah input integer adalah genap.
Contoh 4: Membalikkan String dengan Lambda yang Berparameter
@FunctionalInterface interface MyInterface { String reverse(String s); } public class Main { public static void main(String[] args) { MyInterface ref = (str) -> { String result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; }; System.out.println("Lambda reversed = " + ref.reverse("Lambda")); } }
Output:
Lambda reversed = adbmaL
Antarmuka Fungsional Generik
Antarmuka fungsional dapat bersifat generik, memungkinkan mereka untuk beroperasi pada tipe data apa pun. Contohnya:
@FunctionalInterface interface MyInterface { T func(T t); }
Contoh 5: Generik Lambdas untuk String dan Integer
public class Main { public static void main(String[] args) { // Pembalikan String MyInterface reverse = (str) -> { String result = ""; for (int i = str.length() - 1; i >= 0; i--) { result += str.charAt(i); } return result; }; System.out.println("Lambda reversed = " + reverse.func("Lambda")); // Faktorial Integer MyInterface factorial = (n) -> { int result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; }; System.out.println("factorial of 5 = " + factorial.func(5)); } }
Output:
Lambda reversed = adbmaL factorial of 5 = 120
Ekspresi Lambda dan Stream API
Paket java.util.stream, diperkenalkan di JDK 8, memberdayakan pengembang untuk melakukan operasi gaya fungsional pada koleksi: filtering, mapping, reducing, sorting, dan lainnya.
Pertimbangkan daftar nama tempat, masing-masing diawali dengan negara. Menggunakan streams dan lambda, kita dapat mengekstrak dan memanipulasi data secara efisien.
Contoh 6: Pemrosesan Stream dengan Lambda
import java.util.ArrayList; import java.util.List; public class StreamMain { static List places = new ArrayList<>(); public static List getPlaces() { places.add("Nepa, Kathmandu"); places.add("Nepa, Pokhara"); places.add("India, Delhi"); places.add("USA, New York"); places.add("Africa, Nigeria"); return places; } public static void main(String[] args) { List myPlaces = getPlaces(); System.out.println("Places from Nepal:"); myPlaces.stream() .filter(p -> p.startsWith("Nepa")) .map(p -> p.toUpperCase()) .sorted() .forEach(p -> System.out.println(p)); } }
Output:
Places from Nepal: NEPA, KATHMANDU NEPA, POKHARA
Di sini, rantai operasi stream—filter, map, sorted, forEach—masing-masing menerima lambda, secara dramatis mengurangi boilerplate dibandingkan dengan solusi pra-Java-8.