Dua interface penutup quartet dasar java.util.function bekerja di ujung-ujung arus data. Consumer<T> berada di ujung hilir: menerima satu nilai lewat accept dan tidak mengembalikan apa pun — seluruh gunanya adalah menjalankan aksi terhadap nilai itu, mencetak, mencatat log, atau mengirimnya ke tempat lain. Supplier<T> berada di ujung hulu: tidak menerima masukan sama sekali, tetapi memproduksi satu nilai lewat get — pola dasar factory dan evaluasi lazy. Keduanya tidak menghitung apa pun; mereka menggerakkan data keluar-masuk program kita.
Consumer dan Aksi Tanpa Keluaran #
Method accept(T) mengingatkan kita bahwa tidak semua pekerjaan menghasilkan nilai. Logging, notifikasi, penulisan ke berkas, dan pencetakan ke terminal semuanya bekerja untuk efek sampingnya, bukan untuk nilai kembaliannya — dan Consumer adalah bahasa umum untuk pekerjaan semacam itu. Method andThen juga tersedia: dua Consumer dirangkai sehingga nilai yang sama diproses dua aksi berurutan.
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class AlurKonsumen {
static void catat(String pesan) {
System.out.println("[LOG] " + pesan);
}
public static void main(String[] args) {
// Consumer dasar: satu nilai masuk, tidak ada nilai keluaran
Consumer<String> pencatat = pesan -> catat(pesan);
pencatat.accept("Aplikasi dimulai");
// andThen: dua aksi dijalankan berurutan pada nilai yang sama
Consumer<String> verifikasi = pesan ->
System.out.println("[CEK] " + pesan.length() + " karakter");
Consumer<String> alurLengkap = pencatat.andThen(verifikasi);
alurLengkap.accept("Sinkronisasi data");
// forEach pada List menerima Consumer per elemen
List<String> tugas = List.of("backup", "kirim email", "laporan");
tugas.forEach(item -> System.out.println("- " + item));
// BiConsumer pada Map.forEach: kunci dan nilai sekaligus
Map<String, Integer> stok = new LinkedHashMap<>();
stok.put("kopi", 12);
stok.put("teh", 30);
stok.put("susu", 7);
stok.forEach((barang, jumlah) ->
System.out.println(barang + " : " + jumlah + " unit"));
}
}Output yang Dihasilkan:
[LOG] Aplikasi dimulai
[LOG] Sinkronisasi data
[CEK] 17 karakter
- backup
- kirim email
- laporan
kopi : 12 unit
teh : 30 unit
susu : 7 unitMari kita bedah program tersebut:
- Baris 14-15 (
pencatat.accept(...)): satuStringmasuk, methodcatatdijalankan, tidak ada nilai keluaran. Lambdapesan -> catat(pesan)membungkus method biasa menjadi nilai yang bisa dikirim ke API mana pun yang memintaConsumer<String>. - Baris 18-21 (
alurLengkap = pencatat.andThen(verifikasi)):andThenmenggabungkan dua aksi — catat dulu, periksa panjangnya kemudian. Satu panggilanaccept("Sinkronisasi data")memicu kedua baris keluaran:[LOG] ...lalu[CEK] 17 karakter. - Baris 24-25 (
tugas.forEach(...)):forEachpadaListmenerimaConsumer<T>dan menjalankan sekali per elemen. Lambdaitem -> System.out.println("- " + item)adalahConsumer<String>yang bekerja tanpa kita mendeklarasikan variabel bertipe itu. - Baris 27-33 (
stok.forEach(...)): di sinilahBiConsumer<T, U>bersinar.Map.forEachmengirimkan pasangan kunci-nilai(barang, jumlah)pada setiap iterasi — dua nilai sekaligus, sesuatu yangConsumer<T>tunggal tidak sanggup pegang.LinkedHashMapdipakai agar urutan keluaran mengikuti urutan penyisipan.
Supplier sebagai Factory dan Penunda Eksekusi #
Supplier<T> membalik arah data: tidak ada masukan, hanya keluaran get(). Dua pemakaian utamanya adalah factory (resep pembuatan objek yang bisa dipanggil berulang) dan lazy evaluation (pekerjaan berat ditunda sampai benar-benar diperlukan). Program berikut membuktikan kedua sifat itu dengan penghitung panggilan:
import java.util.function.Supplier;
public class PemasokNilai {
static int jumlahPanggilan = 0;
static String susunLaporan() {
jumlahPanggilan++;
return "Laporan penjualan Q4";
}
public static void main(String[] args) {
Supplier<String> pabrikLaporan = () -> susunLaporan();
// Deklarasi ini TIDAK menjalankan susunLaporan()
Supplier<String> masihRencana = pabrikLaporan;
System.out.println("Sebelum get(): panggilan = " + jumlahPanggilan);
// Barulah di sini laporan benar-benar disusun
String laporan = masihRencana.get();
System.out.println("Setelah get() : panggilan = " + jumlahPanggilan);
System.out.println(laporan);
// Supplier sebagai factory: setiap get() membuat objek baru
Supplier<StringBuilder> pabrikTeks = StringBuilder::new;
StringBuilder a = pabrikTeks.get();
StringBuilder b = pabrikTeks.get();
a.append("terpisah");
System.out.println("a = " + a + ", b kosong? " + (b.length() == 0));
}
}Output yang Dihasilkan:
Sebelum get(): panggilan = 0
Setelah get() : panggilan = 1
Laporan penjualan Q4
a = terpisah, b kosong? trueBedah buktinya baris demi baris:
- Baris 22-24 (
masihRencana = pabrikLaporan): menyalinSupplierke variabel lain tidak menjalankansusunLaporan()— penghitung masih0pada barisSebelum get(). Lambda menyimpan resep, bukan hasil. - Baris 27 (
masihRencana.get()): panggilanget()barulah mengeksekusi resep itu; penghitung naik menjadi1dan laporan jadi. Inilah dasar evaluasi lazy di banyak API — misalnyaOptional.orElseGet(Supplier)yang hanya memanggil supplier ketikaOptionalkosong. - Baris 33-37 (
pabrikTeks = StringBuilder::new): dua panggilanget()menghasilkan dua objekStringBuilderyang berbeda. Setelahadiisi"terpisah", panjangbtetap0— setiapget()menjalankannew StringBuilder()segar, sesuai peran factory. - Baris 5 (
static int jumlahPanggilan): penghitung berupa variabel static sengaja dipakai agar bukti kapan eksekusi terjadi terlihat di keluaran. Variabel semacam ini adalah side effect yang disengaja untuk keperluan pembelajaran — pada kode produksi, penghitungan seperti ini lebih aman dihindari atau diisolasi.
Melengkapi Quartet Dasar #
Dengan Predicate (uji), Function (ubah), Consumer (pakai), dan Supplier (produksi), kita kini memegang keempat kata kerja dasar pemrograman fungsional di Java. Empat kata kerja itu cukup untuk menjelaskan hampir seluruh isi java.util.function — sisanya adalah variasi jumlah parameter, variasi tipe primitif, dan variasi arah masukan-keluaran. Chapter berikutnya mengambil langkah berikutnya yang paling fungsional: merangkai Function-Function menjadi pipeline — dan membedakan andThen dari compose yang urutannya terbalik.
Referensi #
- Oracle: java.util.function.Consumer
- Oracle: java.util.function.Supplier
- Oracle: java.util.function (Package Summary)
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AlurKonsumen.java
Unduh berkasimport java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class AlurKonsumen {
static void catat(String pesan) {
System.out.println("[LOG] " + pesan);
}
public static void main(String[] args) {
// Consumer dasar: satu nilai masuk, tidak ada nilai keluaran
Consumer<String> pencatat = pesan -> catat(pesan);
pencatat.accept("Aplikasi dimulai");
// andThen: dua aksi dijalankan berurutan pada nilai yang sama
Consumer<String> verifikasi = pesan ->
System.out.println("[CEK] " + pesan.length() + " karakter");
Consumer<String> alurLengkap = pencatat.andThen(verifikasi);
alurLengkap.accept("Sinkronisasi data");
// forEach pada List menerima Consumer per elemen
List<String> tugas = List.of("backup", "kirim email", "laporan");
tugas.forEach(item -> System.out.println("- " + item));
// BiConsumer pada Map.forEach: kunci dan nilai sekaligus
Map<String, Integer> stok = new LinkedHashMap<>();
stok.put("kopi", 12);
stok.put("teh", 30);
stok.put("susu", 7);
stok.forEach((barang, jumlah) ->
System.out.println(barang + " : " + jumlah + " unit"));
}
}
PemasokNilai.java
Unduh berkasimport java.util.function.Supplier;
public class PemasokNilai {
static int jumlahPanggilan = 0;
static String susunLaporan() {
jumlahPanggilan++;
return "Laporan penjualan Q4";
}
public static void main(String[] args) {
Supplier<String> pabrikLaporan = () -> susunLaporan();
// Deklarasi ini TIDAK menjalankan susunLaporan()
Supplier<String> masihRencana = pabrikLaporan;
System.out.println("Sebelum get(): panggilan = " + jumlahPanggilan);
// Barulah di sini laporan benar-benar disusun
String laporan = masihRencana.get();
System.out.println("Setelah get() : panggilan = " + jumlahPanggilan);
System.out.println(laporan);
// Supplier sebagai factory: setiap get() membuat objek baru
Supplier<StringBuilder> pabrikTeks = StringBuilder::new;
StringBuilder a = pabrikTeks.get();
StringBuilder b = pabrikTeks.get();
a.append("terpisah");
System.out.println("a = " + a + ", b kosong? " + (b.length() == 0));
}
}