Lambda teks -> teks.length() sudah ringkas, tetapi ada pola yang lebih pendek lagi ketika tubuh lambda hanya memanggil satu method yang sudah ada: cukup sebut nama methodnya dengan tanda pemisah ::. Sintaks itu disebut method reference, dan hasilnya — String::length — dibaca “method length milik class String”. Method reference bukan fitur terpisah dari lambda; keduanya adalah dua penulisan untuk implementasi functional interface yang sama, sehingga semua tempat yang menerima lambda juga menerima method reference. Pada Chapter ini kita akan menguasai empat bentuknya satu per satu.
Empat Bentuk Method Reference #
Tabel berikut merangkum keempat bentuk beserta maknanya:
| Bentuk | Contoh | Setara dengan lambda |
|---|---|---|
| Static method | Integer::parseInt | teks -> Integer.parseInt(teks) |
| Bound instance method | System.out::println | teks -> System.out.println(teks) |
| Unbound instance method | String::length | teks -> teks.length() |
| Constructor reference | ArrayList::new | () -> new ArrayList<>() |
Perbedaan terpenting ada di baris kedua dan ketiga. Pada bound reference, objek yang menjadi pemilik method sudah ditentukan saat referensi dibuat (System.out dijepit lebih dulu). Pada unbound reference, objeknya justru datang sebagai parameter dari method abstrak — String::length berarti “panggil length() pada argumen pertama yang masuk”. Constructor reference (ArrayList::new) akan kita bahas lebih dalam pada Chapter berikutnya; di sini kita perlakukan sebagai bentuk keempat yang melengkapi keluarga ::.
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class EmpatReferensi {
public static void main(String[] args) {
// Bentuk 1: static method reference -- Class::methodStatic
Function<String, Integer> parser = Integer::parseInt;
System.out.println(parser.apply("42") + 58);
// Bentuk 2: bound instance method reference -- instansi::method
Consumer<String> pencetak = System.out::println;
pencetak.accept("Dicetak lewat method reference");
// Bentuk 3: unbound instance method reference -- Class::methodInstansi
Function<String, Integer> ukuran = String::length;
System.out.println(ukuran.apply("deklaratif"));
// Bentuk 4: constructor reference -- Class::new
Supplier<List<String>> pabrikDaftar = ArrayList::new;
List<String> daftar = pabrikDaftar.get();
daftar.add("satu");
daftar.add("dua");
System.out.println(daftar);
}
}Output yang Dihasilkan:
100
Dicetak lewat method reference
10
[satu, dua]Mari kita bedah keempat bentuk pada program di atas:
- Baris 10 (
Function<String, Integer> parser = Integer::parseInt;): method staticInteger.parseInt(String)dipetakan keapply(String). Saatparser.apply("42")dipanggil, JVM menjalankanInteger.parseInt("42")— hasilnya42, lalu ditambah58menjadi100. - Baris 14 (
Consumer<String> pencetak = System.out::println;): objekSystem.outterjepit saat baris ini dieksekusi. Method abstrakaccept(String)menjadi panggilanSystem.out.println(String)pada objek yang sama setiap kali dipanggil. - Baris 18 (
Function<String, Integer> ukuran = String::length;): tidak ada objek spesifik —String::lengthberarti “terima satuString, panggillength()padanya”. Argumenapply("deklaratif")menjadi penerima methodlength(), menghasilkan10. - Baris 22-26 (
Supplier<List<String>> pabrikDaftar = ArrayList::new;): constructor tanpa argumen dipetakan keSupplier.get(). Setiap panggilanpabrikDaftar.get()menjalankannew ArrayList<>()dan mengembalikan list kosong yang baru — bukan list yang sama — sehinggadaftarbisa diisi"satu"dan"dua".
Method Reference di Tempat Kerjanya #
Kekuatan method reference paling terasa ketika dipakai sebagai argumen langsung, bukan disimpan di variabel. Kode berikut memakai System.out::println pada forEach dan membandingkan dua penulisan yang setara penuh untuk kapitalisasi string:
import java.util.List;
import java.util.function.UnaryOperator;
public class LambdaVsReferensi {
public static void main(String[] args) {
List<String> nama = List.of("budi", "siti", "dewi");
// Method reference sebagai argumen langsung
System.out.println("-- forEach(System.out::println) --");
nama.forEach(System.out::println);
// Dua penulisan yang setara penuh
UnaryOperator<String> kapitalLambda = s -> s.toUpperCase();
UnaryOperator<String> kapitalReferensi = String::toUpperCase;
System.out.println("-- lambda vs method reference --");
for (String n : nama) {
System.out.println(kapitalLambda.apply(n) + " / " + kapitalReferensi.apply(n));
}
}
}Output yang Dihasilkan:
-- forEach(System.out::println) --
budi
siti
dewi
-- lambda vs method reference --
BUDI / BUDI
SITI / SITI
DEWI / DEWIDua pengamatan dari hasil eksekusi tersebut:
- Baris 9 (
nama.forEach(System.out::println)):forEachpadaList<String>menerimaConsumer<String>. Method reference mengisi slot itu tanpa satu variabel pun — pembaca kode langsung menangkap maksudnya “cetak setiap elemen”. - Baris 12-13:
s -> s.toUpperCase()danString::toUpperCasemenghasilkan keluaran identik pada ketiga nama. Tidak ada perbedaan bytecode semantik keduanya — pilihan jatuh pada selera dan keterbacaan.
Aturan Pemetaan yang Perlu Dihafal #
Method reference mengikuti target type persis seperti lambda, dengan satu aturan tambahan: daftar parameter method abstrak harus cocok dengan daftar parameter method yang dirujuk. Function<String, Integer> cocok dengan String::length karena apply(String) menerima satu parameter dan length() mengembalikan int yang di-autobox menjadi Integer. Method dengan dua argumen seperti Integer.compare(int, int) tidak akan cocok dengan Function<String, Integer>; method tersebut cocok dengan Comparator<Integer> karena compare(Integer, Integer) menerima dua parameter dan hasilnya langsung dipakai sebagai nilai kembalian. Bila tanda kurung kurawal, kurung parameter, dan kata kunci return mulai memenuhi layar, itulah saatnya bertanya: apakah method yang sudah ada bisa dirujuk dengan ::.
Dengan empat bentuk :: di tangan, tersisa satu variasi yang belum dibedah tuntas: bentuk keempat itu sendiri. Chapter berikutnya fokus penuh pada constructor reference — dari ArrayList::new sampai constructor berargumen dan pemakaian factory-nya pada API pustaka standar.
Referensi #
- dev.java: Lambda Expressions
- Oracle: java.util.function (Package Summary)
- Java Language Specification (JLS) — Bab 15
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
EmpatReferensi.java
Unduh berkasimport java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class EmpatReferensi {
public static void main(String[] args) {
// Bentuk 1: static method reference -- Class::methodStatic
Function<String, Integer> parser = Integer::parseInt;
System.out.println(parser.apply("42") + 58);
// Bentuk 2: bound instance method reference -- instansi::method
Consumer<String> pencetak = System.out::println;
pencetak.accept("Dicetak lewat method reference");
// Bentuk 3: unbound instance method reference -- Class::methodInstansi
Function<String, Integer> ukuran = String::length;
System.out.println(ukuran.apply("deklaratif"));
// Bentuk 4: constructor reference -- Class::new
Supplier<List<String>> pabrikDaftar = ArrayList::new;
List<String> daftar = pabrikDaftar.get();
daftar.add("satu");
daftar.add("dua");
System.out.println(daftar);
}
}
LambdaVsReferensi.java
Unduh berkasimport java.util.List;
import java.util.function.UnaryOperator;
public class LambdaVsReferensi {
public static void main(String[] args) {
List<String> nama = List.of("budi", "siti", "dewi");
// Method reference sebagai argumen langsung
System.out.println("-- forEach(System.out::println) --");
nama.forEach(System.out::println);
// Dua penulisan yang setara penuh
UnaryOperator<String> kapitalLambda = s -> s.toUpperCase();
UnaryOperator<String> kapitalReferensi = String::toUpperCase;
System.out.println("-- lambda vs method reference --");
for (String n : nama) {
System.out.println(kapitalLambda.apply(n) + " / " + kapitalReferensi.apply(n));
}
}
}