Setiap keputusan dalam program — pelamar lolos atau tidak, pesanan valid atau ditolak — pada dasarnya adalah fungsi dari nilai menuju boolean. Predicate<T> mengangkat pola itu menjadi nilai kelas satu: kondisi yang bisa disimpan di variabel, dikirim ke method, dan paling penting — digabungkan dengan operasi and, or, serta negate tanpa menulis satu pernyataan if baru. Pada Chapter ini kita membedah Predicate sebagai interface pertama dari trio java.util.function yang akan kita dalami, lalu menerapkannya untuk menyaring daftar dan memvalidasi pesanan.
Menguji Nilai dengan test #
Predicate<T> punya satu method abstrak: boolean test(T t) — berikan satu nilai, terima jawaban true atau false. Kekuatan sesungguhnya muncul dari tiga method default kombinatornya: and (kedua kondisi harus benar), or (salah satu cukup), dan negate (membalik hasil). Ketiganya mengembalikan Predicate baru, sehingga kondisi majemuk dibangun dengan menyusun potongan-potongan kecil — persis seperti operator logika &&, ||, dan !, tetapi dalam level nilai, bukan level statement.
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class SaringPelamar {
// Filter manual: syarat datang sebagai Predicate, loop tetap eksplisit
static List<String> saring(List<String> data, Predicate<String> syarat) {
List<String> hasil = new ArrayList<>();
for (String item : data) {
if (syarat.test(item)) {
hasil.add(item);
}
}
return hasil;
}
public static void main(String[] args) {
List<String> pelamar = List.of("Budi", "Siti", "Dewi", "Ani", "Rahmat");
Predicate<String> panjangMinimal = nama -> nama.length() >= 4;
Predicate<String> diawaliHurufD = nama -> nama.startsWith("D");
System.out.println("Panjang >= 4 : " + saring(pelamar, panjangMinimal));
System.out.println("Diawali huruf D : " + saring(pelamar, diawaliHurufD));
System.out.println("and : " + saring(pelamar, panjangMinimal.and(diawaliHurufD)));
System.out.println("or : " + saring(pelamar, panjangMinimal.or(diawaliHurufD)));
System.out.println("negate : " + saring(pelamar, diawaliHurufD.negate()));
// BiPredicate: dua nilai masuk, keputusan keluar
BiPredicate<String, Integer> panjangTepat = (nama, n) -> nama.length() == n;
System.out.println("Dewi 4 huruf? : " + panjangTepat.test("Dewi", 4));
}
}Output yang Dihasilkan:
Panjang >= 4 : [Budi, Siti, Dewi, Rahmat]
Diawali huruf D : [Dewi]
and : [Dewi]
or : [Budi, Siti, Dewi, Rahmat]
negate : [Budi, Siti, Ani, Rahmat]
Dewi 4 huruf? : trueMari kita bedah program tersebut baris demi baris:
- Baris 7-14 (method
saring): ini adalah filter buatan sendiri — loopforeksplisit yang memanggilsyarat.test(item)untuk setiap elemen. Perhatikan bahwa method ini tidak tahu sedang menyaring apa; seluruh kebijakan hidup di dalamPredicateyang dikirim pemanggil. - Baris 20-21: dua kondisi dasar dibuat terpisah:
panjangMinimal(nama minimal empat huruf) dandiawaliHurufD. Keduanya adalahPredicate<String>biasa — nilai boolean yang bisa dibawa ke mana saja. - Baris 23-24: dua pemanggilan
saringpertama memakai kondisi dasar berdiri sendiri. Hasilnya dua laporan berbeda dari satu method penyaring. - Baris 25 (
panjangMinimal.and(diawaliHurufD)):andmenggabungkan dua kondisi menjadiPredicatebaru yang hanya meloloskan item yang lolos keduanya — dari kelima pelamar hanya"Dewi"yang panjangnya minimal empat dan diawali huruf D. - Baris 26 (
or) dan Baris 27 (negate):ormeloloskan gabungan kedua himpunan, sedangkandiawaliHurufD.negate()membalik kondisi menjadi “bukan yang diawali huruf D”. Perhatikan bahwanegatebekerja tanpa menulisnama -> !nama.startsWith("D")— kondisi lama dipakai ulang apa adanya. - Baris 30-31 (
BiPredicate<String, Integer>): varian dua masukan.test("Dewi", 4)menjawab kecocokan pasangan nilai — berguna saat keputusan membutuhkan dua data sekaligus, misalnya membandingkan kunci dengan tabel aturan.
Predicate di Dunia Validasi #
Pola kombinasi Predicate paling sering dipakai untuk aturan validasi berlapis. Program berikut memeriksa daftar pesanan terhadap dua aturan bisnis; aturan disimpan dalam List<Predicate<Pesanan>> sehingga menambah aturan baru tidak menyentuh method pemeriksanya:
import java.util.List;
import java.util.function.Predicate;
public class ValidasiPesanan {
record Pesanan(String id, int jumlah, double total) {}
static void periksa(Pesanan p, List<Predicate<Pesanan>> aturan) {
for (Predicate<Pesanan> aturanSatu : aturan) {
if (!aturanSatu.test(p)) {
System.out.println(p.id() + " DITOLAK");
return;
}
}
System.out.println(p.id() + " LOLOS");
}
public static void main(String[] args) {
Predicate<Pesanan> jumlahWajar = p -> p.jumlah() > 0 && p.jumlah() <= 100;
Predicate<Pesanan> totalWajar = p -> p.total() >= 0;
List<Pesanan> pesanan = List.of(
new Pesanan("A-1", 3, 75000),
new Pesanan("A-2", 0, 5000),
new Pesanan("A-3", 250, 120000));
for (Pesanan p : pesanan) {
periksa(p, List.of(jumlahWajar, totalWajar));
}
}
}Output yang Dihasilkan:
A-1 LOLOS
A-2 DITOLAK
A-3 DITOLAKTiga hal patut dicermati dari berkas tersebut:
- Baris 6 (
record Pesanan(...)): record menyediakan aksesor ringkas (p.jumlah(),p.total()) — pas untuk data yang hanya diperiksa, tidak diubah. - Baris 8-18 (method
periksa): loop menjalankan setiap aturan lewattest; pesanan pertama yang gagal langsung menghasilkanDITOLAK. Method ini sepenuhnya generik: daftar aturan datang dari luar, jadiperiksatidak perlu berubah ketika kebijakan bisnis bertambah. - Baris 21-22: dua aturan dideklarasikan sebagai variabel bernama. Nama
jumlahWajardantotalWajarmembuat daftar aturan terbaca seperti dokumen kebijakan — bandingkan dengan satuifraksasa berisi enam perbandingan yang harus dibedah pembaca baris demi baris.
Predicate untuk Dua Nilai #
BiPredicate<T, U> meluaskan gagasan pengujian ke dua nilai dengan tipe berbeda — misalnya mencocokkan String dengan Integer, atau entitas dengan konteks. Kombinator and/or/negate juga tersedia pada BiPredicate, jadi pasangan kondisi dua-nilai tetap bisa disusun deklaratif. Untuk tiga nilai atau lebih, konvensi Java mengarahkan kita membungkus nilai-nilai itu dalam satu record lalu memakai Predicate<Record> tunggal — pendekatan yang lebih mudah dibaca daripada interface berparameter panjang.
Dari kondisi boolean kita naik satu tingkat: bagaimana jika hasilnya bukan sekadar true/false, melainkan nilai baru? Chapter berikutnya membahas Function dan BiFunction — mesin transformasi yang menjadi bahan bakar utama pipeline fungsional.
Referensi #
- Oracle: java.util.function.Predicate
- Oracle: java.util.function (Package Summary)
- dev.java: Lambda Expressions
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
SaringPelamar.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class SaringPelamar {
// Filter manual: syarat datang sebagai Predicate, loop tetap eksplisit
static List<String> saring(List<String> data, Predicate<String> syarat) {
List<String> hasil = new ArrayList<>();
for (String item : data) {
if (syarat.test(item)) {
hasil.add(item);
}
}
return hasil;
}
public static void main(String[] args) {
List<String> pelamar = List.of("Budi", "Siti", "Dewi", "Ani", "Rahmat");
Predicate<String> panjangMinimal = nama -> nama.length() >= 4;
Predicate<String> diawaliHurufD = nama -> nama.startsWith("D");
System.out.println("Panjang >= 4 : " + saring(pelamar, panjangMinimal));
System.out.println("Diawali huruf D : " + saring(pelamar, diawaliHurufD));
System.out.println("and : " + saring(pelamar, panjangMinimal.and(diawaliHurufD)));
System.out.println("or : " + saring(pelamar, panjangMinimal.or(diawaliHurufD)));
System.out.println("negate : " + saring(pelamar, diawaliHurufD.negate()));
// BiPredicate: dua nilai masuk, keputusan keluar
BiPredicate<String, Integer> panjangTepat = (nama, n) -> nama.length() == n;
System.out.println("Dewi 4 huruf? : " + panjangTepat.test("Dewi", 4));
}
}
ValidasiPesanan.java
Unduh berkasimport java.util.List;
import java.util.function.Predicate;
public class ValidasiPesanan {
record Pesanan(String id, int jumlah, double total) {}
static void periksa(Pesanan p, List<Predicate<Pesanan>> aturan) {
for (Predicate<Pesanan> aturanSatu : aturan) {
if (!aturanSatu.test(p)) {
System.out.println(p.id() + " DITOLAK");
return;
}
}
System.out.println(p.id() + " LOLOS");
}
public static void main(String[] args) {
Predicate<Pesanan> jumlahWajar = p -> p.jumlah() > 0 && p.jumlah() <= 100;
Predicate<Pesanan> totalWajar = p -> p.total() >= 0;
List<Pesanan> pesanan = List.of(
new Pesanan("A-1", 3, 75000),
new Pesanan("A-2", 0, 5000),
new Pesanan("A-3", 250, 120000));
for (Pesanan p : pesanan) {
periksa(p, List.of(jumlahWajar, totalWajar));
}
}
}