Jika Predicate menjawab “ya atau tidak”, Function<T, R> menjawab “menjadi apa?” — interface ini adalah mesin transformasi universal gaya fungsional: terima satu nilai bertipe T, keluarkan nilai baru bertipe R. Method abstraknya bernama apply, dan tipe masukan serta keluaran boleh berbeda sehingga Function<String, Integer> (urai teks menjadi angka) sama sahnya dengan Function<Double, Double> (terapkan pajak). Pada Chapter ini kita menyelami apply, sepupu dua-masukannya BiFunction, dua spesialisasinya UnaryOperator dan BinaryOperator, serta gerbang menuju dunia komposisi: method andThen.
Method apply sebagai Mesin Transformasi #
Cara memakai Function paling dasar adalah memanggil apply(nilai). Kompilator memastikan tipe argumen cocok dengan tipe masukan dan hasilnya membawa tipe keluaran — kesalahan tipe tertangkap sebelum program berjalan, sama seperti method biasa. Yang membedakan adalah nilai fungsi itu sendiri bisa berpindah: disimpan di variabel, dikirim sebagai argumen, diambil dari Map, atau diproduksi oleh method lain.
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class MesinTransformasi {
public static void main(String[] args) {
// Function dasar: satu masukan String, keluaran Integer
Function<String, Integer> parser = Integer::parseInt;
System.out.println(parser.apply("99"));
// andThen: keluaran parser menjadi masukan lambda berikutnya
Function<String, Integer> parserKaliDua = parser.andThen(n -> n * 2);
System.out.println(parserKaliDua.apply("99"));
// BiFunction: dua masukan, satu keluaran
BiFunction<Integer, Integer, Integer> pangkat =
(dasar, eksponen) -> (int) Math.pow(dasar, eksponen);
System.out.println(pangkat.apply(2, 10));
// UnaryOperator: masukan dan keluaran bertipe sama
UnaryOperator<String> rapikan = s -> s.trim().toLowerCase();
System.out.println("[" + rapikan.apply(" Halo Java ") + "]");
// BinaryOperator: dua masukan bertipe sama, keluaran bertipe sama
BinaryOperator<String> gabung = (a, b) -> a + ", " + b;
System.out.println(gabung.apply("imperatif", "deklaratif"));
}
}Output yang Dihasilkan:
99
198
1024
[halo java]
imperatif, deklaratifMari kita bedah kelima penggalan pada program di atas:
- Baris 9-10 (
Function<String, Integer> parser): method referenceInteger::parseIntmengisiapply(String). Panggilanparser.apply("99")menjalankanInteger.parseInt("99")dan mengembalikan99sebagaiInteger. - Baris 13-14 (
parser.andThen(n -> n * 2)):andThenmenggabungkan duaFunctionmenjadi satu — keluaranparsermengalir langsung menjadi masukann -> n * 2. Hasil"99"berubah menjadi198dalam satu panggilanparserKaliDua.apply("99"). Ini adalah contoh pertama komposisi, topik yang akan kita bedah penuh pada Chapter Function Composition. - Baris 17-19 (
BiFunction<Integer, Integer, Integer> pangkat):BiFunction<T, U, R>menerima dua nilai bertipe berbeda — di sini dasar dan eksponen — laluapply(2, 10)mengembalikan1024(hasilMath.powyang dipaksa kembali keint). - Baris 22-23 (
UnaryOperator<String> rapikan): spesialisasiFunction<T, T>ketika masukan dan keluaran bertipe sama. Rantaitrim().toLowerCase()pada" Halo Java "menghasilkan"halo java"— tanda kurung siku pada keluaran hanya penanda bahwa spasi di awal dan akhir sudah bersih. - Baris 26-27 (
BinaryOperator<String> gabung): spesialisasiBiFunction<T, T, T>ketika ketiga tipe sama. DuaStringmasuk, satuStringkeluar — pola khas untuk operasi penggabungan.
Function dalam Perhitungan Nyata #
Kekuatan Function terlihat ketika transformasi kecil dirangkai menjadi alur bisnis. Program berikut menghitung tagihan: diskon (jika ada), pajak 11%, lalu pemformatan rupiah — masing-masing satu Function mandiri yang dirakit menjadi satu:
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class HargaFinal {
public static void main(String[] args) {
UnaryOperator<Double> tambahPajak = harga -> harga * 1.11;
Function<Double, String> formatRupiah = harga -> String.format("Rp%,.0f", harga);
BiFunction<Double, Double, Double> terapkanDiskon =
(harga, persen) -> harga * (1 - persen);
// Rantai transformasi: pajak dulu, lalu format
Function<Double, String> tagihan = tambahPajak.andThen(formatRupiah);
System.out.println("Tanpa diskon : " + tagihan.apply(200000.0));
// BiFunction menyala lebih dulu, hasilnya masuk ke rantai Function
double setelahDiskon = terapkanDiskon.apply(200000.0, 0.2);
System.out.println("Diskon 20% : " + tagihan.apply(setelahDiskon));
}
}Output yang Dihasilkan:
Tanpa diskon : Rp222,000
Diskon 20% : Rp177,600Bedah alurnya:
- Baris 8-11: tiga mesin dideklarasikan terpisah —
tambahPajak(UnaryOperator<Double>),formatRupiah(Function<Double, String>), danterapkanDiskon(BiFunction<Double, Double, Double>). Setiap mesin bisa diuji sendiri dan dipakai ulang di perhitungan lain. - Baris 14-15 (
tambahPajak.andThen(formatRupiah)): rantaitagihanmenerimaDoubleharga dan mengeluarkanStringsiap cetak. Pemanggilantagihan.apply(200000.0)mengalirkan200000.0→222000.0(pajak) →"Rp222,000"(format). Angka di sisi keluaran format memakai pemisah ribuan karena localeen_USaktif di JVM. - Baris 18-19:
terapkanDiskon.apply(200000.0, 0.2)menghasilkan160000.0, lalu nilai itu masuk ke rantaitagihanyang sama — bukti bahwaBiFunctiondanFunctionhidup dalam satu ekosistem: keluaran yang satu adalah masukan yang lain. - Perhitungan pecahan:
200000.0 * 1.11secara matematis biner tidak tepat (222000.00000000003), tetapiString.format("Rp%,.0f", ...)membulatkan keRp222,000sehingga keluaran tetap rapi dan deterministik. Kita sengaja memformat di ujung pipeline, bukan membulatkan di tengah, agar presisi terjaga sepanjang rantai.
Dari apply ke Komposisi #
Seluruh contoh pada Chapter ini memakai apply sebagai pemicu dan andThen sebagai penggabung. Dua kata kunci itu sudah cukup untuk membangun pipeline berlapis, tetapi Java menyediakan satu lagi penggabung bernama compose — dan urutan keduanya berbeda. Membedakan f.andThen(g) dari f.compose(g) adalah pengetahuan yang sering tertukar bahkan oleh programmer berpengalaman, sehingga Chapter berikutnya mengabdikan seluruh bagiannya untuk kontras kedua method tersebut dengan bukti eksekusi yang bisa dilihat mata.
Referensi #
- Oracle: java.util.function.Function
- Oracle: java.util.function.BiFunction
- dev.java: Lambda Expressions
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
HargaFinal.java
Unduh berkasimport java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class HargaFinal {
public static void main(String[] args) {
UnaryOperator<Double> tambahPajak = harga -> harga * 1.11;
Function<Double, String> formatRupiah = harga -> String.format("Rp%,.0f", harga);
BiFunction<Double, Double, Double> terapkanDiskon =
(harga, persen) -> harga * (1 - persen);
// Rantai transformasi: pajak dulu, lalu format
Function<Double, String> tagihan = tambahPajak.andThen(formatRupiah);
System.out.println("Tanpa diskon : " + tagihan.apply(200000.0));
// BiFunction menyala lebih dulu, hasilnya masuk ke rantai Function
double setelahDiskon = terapkanDiskon.apply(200000.0, 0.2);
System.out.println("Diskon 20% : " + tagihan.apply(setelahDiskon));
}
}
MesinTransformasi.java
Unduh berkasimport java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class MesinTransformasi {
public static void main(String[] args) {
// Function dasar: satu masukan String, keluaran Integer
Function<String, Integer> parser = Integer::parseInt;
System.out.println(parser.apply("99"));
// andThen: keluaran parser menjadi masukan lambda berikutnya
Function<String, Integer> parserKaliDua = parser.andThen(n -> n * 2);
System.out.println(parserKaliDua.apply("99"));
// BiFunction: dua masukan, satu keluaran
BiFunction<Integer, Integer, Integer> pangkat =
(dasar, eksponen) -> (int) Math.pow(dasar, eksponen);
System.out.println(pangkat.apply(2, 10));
// UnaryOperator: masukan dan keluaran bertipe sama
UnaryOperator<String> rapikan = s -> s.trim().toLowerCase();
System.out.println("[" + rapikan.apply(" Halo Java ") + "]");
// BinaryOperator: dua masukan bertipe sama, keluaran bertipe sama
BinaryOperator<String> gabung = (a, b) -> a + ", " + b;
System.out.println(gabung.apply("imperatif", "deklaratif"));
}
}