Selama perjalanan kita sejauh ini, kode Java yang kita tulis hampir selalu berbentuk perintah berurutan: buat variabel, ulangi daftar, ubah isi variabel, cetak hasil. Gaya tersebut disebut imperatif — kita menuliskan bagaimana komputer harus bekerja, langkah demi langkah. Sejak Java 8 (rilis 2014), Java membawa perangkat gaya fungsional — lambda expression, method reference, dan paket java.util.function — sehingga kita boleh menuliskan apa yang kita inginkan dan membiarkan pustaka standar yang mengurus detail langkahnya. Pada Section ini kita akan membangun fondasi gaya fungsional tersebut dari nol: dari functional interface, lambda, sampai pola desain yang memanfaatkannya.
Gaya Imperatif vs Gaya Deklaratif #
Perbedaan kedua gaya paling mudah dilihat pada tugas yang sama: menyaring nilai ujian yang lalu (nilai >= 70) dari sebuah daftar. Gaya imperatif memakai loop for eksplisit, variabel penampung yang isinya berubah-ubah, dan pernyataan if di dalam loop. Gaya deklaratif mendeskripsikan hasil akhirnya — “saring yang nilainya minimal 70” — melalui stream() dan filter(); loop, pengecekan, dan penyalinan hasil dikerjakan oleh pustaka.
import java.util.ArrayList;
import java.util.List;
public class DuaGayaFilter {
public static void main(String[] args) {
List<Integer> nilaiUjian = List.of(45, 78, 92, 61, 88, 30, 73);
// Gaya imperatif: menulis BAGAIMANA, langkah demi langkah
List<Integer> lulusImperatif = new ArrayList<>();
for (int nilai : nilaiUjian) {
if (nilai >= 70) {
lulusImperatif.add(nilai);
}
}
// Gaya deklaratif: mendeskripsikan APA yang diinginkan
List<Integer> lulusDeklaratif = nilaiUjian.stream()
.filter(nilai -> nilai >= 70)
.toList();
System.out.println("Imperatif : " + lulusImperatif);
System.out.println("Deklaratif : " + lulusDeklaratif);
}
}Output yang Dihasilkan:
Imperatif : [78, 92, 88, 73]
Deklaratif : [78, 92, 88, 73]Mari kita bedah kedua blok pada program di atas:
- Baris 10-16 (blok imperatif): kita menginisialisasi
lulusImperatifsebagaiArrayListkosong, lalu mengendalikan sendiri iterasinya — satu per satu nilai diperiksaif (nilai >= 70), dan yang lolos ditambahkan denganadd. Seluruh mekanisme loop menjadi tanggung jawab kode kita. - Baris 18-21 (blok deklaratif):
nilaiUjian.stream()membuka aliran data,filter(nilai -> nilai >= 70)menerima predikat (kondisi) berbentuk lambda, dantoList()mengemas hasilnya. Kode membaca seperti spesifikasi masalah, bukan reka ulang mekanisme. - Baris 23-24: kedua hasil dicetak dan terbukti identik — dua gaya, satu jawaban. Bagian
stream()sendiri akan kita bahas tuntas pada Section Stream API; untuk sekarang, fokus kita adalah cara berpikirnya.
Motivasi: Komposisi dan Kejelasan #
Dua keunggulan utama gaya fungsional adalah komposisi (menyusun potongan kecil menjadi perilaku besar) dan kejelasan (kode membaca seperti deskripsi masalah). Contoh berikut menyuntikkan behavior ke dalam method pilih lewat parameter bertipe Predicate<Transaksi> — sebuah “kondisi” yang bisa dikombinasikan dengan operator and:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class ManfaatKomposisi {
record Transaksi(String kategori, int jumlah) {}
// Behavior (kriteria) disuntik sebagai parameter, bukan ditulis ulang
static List<Transaksi> pilih(List<Transaksi> data, Predicate<Transaksi> kriteria) {
List<Transaksi> hasil = new ArrayList<>();
for (Transaksi t : data) {
if (kriteria.test(t)) {
hasil.add(t);
}
}
return hasil;
}
public static void main(String[] args) {
List<Transaksi> transaksi = List.of(
new Transaksi("makan", 25000),
new Transaksi("transportasi", 12000),
new Transaksi("makan", 40000),
new Transaksi("hiburan", 60000));
Predicate<Transaksi> kategoriMakan = t -> t.kategori().equals("makan");
Predicate<Transaksi> jumlahBesar = t -> t.jumlah() > 30000;
System.out.println("Transaksi makan : " + pilih(transaksi, kategoriMakan).size());
System.out.println("Transaksi > 30 ribu : " + pilih(transaksi, jumlahBesar).size());
System.out.println("Makan DAN > 30 ribu : " + pilih(transaksi, kategoriMakan.and(jumlahBesar)).size());
}
}Output yang Dihasilkan:
Transaksi makan : 2
Transaksi > 30 ribu : 2
Makan DAN > 30 ribu : 1Tiga hal penting terjadi di berkas tersebut:
- Baris 10 (
static List<Transaksi> pilih(...)): methodpilihmenerima daftar transaksi dan sebuahPredicate<Transaksi>. Method ini tidak tahu kriterianya apa — kriteria datang dari luar sebagai nilai (behavior sebagai data). - Baris 25-26: dua kondisi dibuat sebagai variabel terpisah (
kategoriMakandanjumlahBesar), lalu dipakai berdiri sendiri maupun digabung dengankategoriMakan.and(jumlahBesar)tanpa menulis method penyaring baru. Inilah komposisi: potongan kecil dirangkai menjadi potongan besar. - Baris 28-30: tiga laporan berbeda dihasilkan dari satu method
pilih. Pada gaya imperatif murni, kita umumnya menulis tiga loop yang hampir sama — tiga tempat yang harus diperiksa bila aturan berubah.
Java Tetap Bahasa Berorientasi Objek #
Java bukan Haskell atau Clojure. Java tetap OOP-first: seluruh lambda pada akhirnya adalah objek yang mengimplementasikan sebuah interface, kode tetap hidup di dalam class, dan tipe tetap diperiksa compiler javac pada saat kompilasi. Yang berubah sejak Java 8 adalah bahasa mengizinkan behavior diperlakukan sebagai nilai — disimpan di variabel, dikirim sebagai argumen, dan dikembalikan dari method — tanpa ritual anonymous class yang panjang.
Karena itu, gaya fungsional di Java paling tepat dipandang sebagai perangkat tambahan di tangan kita, bukan pengganti total: manipulasi koleksi, validasi berlapis, dan pembuatan pipeline data memang pas dengan gaya fungsional, sementara pemodelan domain bisnis tetap nyaman dengan class dan interface. Programmer Java produktif menguasai keduanya dan memilih sesuai masalah.
Peta Section Ini #
Berikut rute belajar kita pada Section ini:
- Functional Interfaces — kontrak single abstract method (SAM) yang menjadi fondasi lambda.
- Lambda Expressions & References — tiga cara menuliskan behavior: lambda, method reference, constructor reference.
- Paket
java.util.function— interface bawaan:Predicate,Function,Consumer,Supplier, beserta kombinasinya. - Komposisi & Ketetapan —
compose/andThen, aturan effectively final, pure function, dan pola desain fungsional.
Semuanya berangkat dari satu konsep kunci: functional interface. Mari kita mulai dari sana.
Referensi #
- Oracle: java.util.function (Package Summary)
- dev.java: Lambda Expressions
- Java Language Specification (JLS) — Bab 15
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
DuaGayaFilter.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
public class DuaGayaFilter {
public static void main(String[] args) {
List<Integer> nilaiUjian = List.of(45, 78, 92, 61, 88, 30, 73);
// Gaya imperatif: menulis BAGAIMANA, langkah demi langkah
List<Integer> lulusImperatif = new ArrayList<>();
for (int nilai : nilaiUjian) {
if (nilai >= 70) {
lulusImperatif.add(nilai);
}
}
// Gaya deklaratif: mendeskripsikan APA yang diinginkan
List<Integer> lulusDeklaratif = nilaiUjian.stream()
.filter(nilai -> nilai >= 70)
.toList();
System.out.println("Imperatif : " + lulusImperatif);
System.out.println("Deklaratif : " + lulusDeklaratif);
}
}
ManfaatKomposisi.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class ManfaatKomposisi {
record Transaksi(String kategori, int jumlah) {}
// Behavior (kriteria) disuntik sebagai parameter, bukan ditulis ulang
static List<Transaksi> pilih(List<Transaksi> data, Predicate<Transaksi> kriteria) {
List<Transaksi> hasil = new ArrayList<>();
for (Transaksi t : data) {
if (kriteria.test(t)) {
hasil.add(t);
}
}
return hasil;
}
public static void main(String[] args) {
List<Transaksi> transaksi = List.of(
new Transaksi("makan", 25000),
new Transaksi("transportasi", 12000),
new Transaksi("makan", 40000),
new Transaksi("hiburan", 60000));
Predicate<Transaksi> kategoriMakan = t -> t.kategori().equals("makan");
Predicate<Transaksi> jumlahBesar = t -> t.jumlah() > 30000;
System.out.println("Transaksi makan : " + pilih(transaksi, kategoriMakan).size());
System.out.println("Transaksi > 30 ribu : " + pilih(transaksi, jumlahBesar).size());
System.out.println("Makan DAN > 30 ribu : " + pilih(transaksi, kategoriMakan.and(jumlahBesar)).size());
}
}