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:

BentukContohSetara dengan lambda
Static methodInteger::parseIntteks -> Integer.parseInt(teks)
Bound instance methodSystem.out::printlnteks -> System.out.println(teks)
Unbound instance methodString::lengthteks -> teks.length()
Constructor referenceArrayList::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:

text
100
Dicetak lewat method reference
10
[satu, dua]

Mari kita bedah keempat bentuk pada program di atas:

  1. Baris 10 (Function<String, Integer> parser = Integer::parseInt;): method static Integer.parseInt(String) dipetakan ke apply(String). Saat parser.apply("42") dipanggil, JVM menjalankan Integer.parseInt("42") — hasilnya 42, lalu ditambah 58 menjadi 100.
  2. Baris 14 (Consumer<String> pencetak = System.out::println;): objek System.out terjepit saat baris ini dieksekusi. Method abstrak accept(String) menjadi panggilan System.out.println(String) pada objek yang sama setiap kali dipanggil.
  3. Baris 18 (Function<String, Integer> ukuran = String::length;): tidak ada objek spesifik — String::length berarti “terima satu String, panggil length() padanya”. Argumen apply("deklaratif") menjadi penerima method length(), menghasilkan 10.
  4. Baris 22-26 (Supplier<List<String>> pabrikDaftar = ArrayList::new;): constructor tanpa argumen dipetakan ke Supplier.get(). Setiap panggilan pabrikDaftar.get() menjalankan new ArrayList<>() dan mengembalikan list kosong yang baru — bukan list yang sama — sehingga daftar bisa 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:

text
-- forEach(System.out::println) --
budi
siti
dewi
-- lambda vs method reference --
BUDI / BUDI
SITI / SITI
DEWI / DEWI

Dua pengamatan dari hasil eksekusi tersebut:

  1. Baris 9 (nama.forEach(System.out::println)): forEach pada List<String> menerima Consumer<String>. Method reference mengisi slot itu tanpa satu variabel pun — pembaca kode langsung menangkap maksudnya “cetak setiap elemen”.
  2. Baris 12-13: s -> s.toUpperCase() dan String::toUpperCase menghasilkan 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 #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

EmpatReferensi.java

Unduh berkas
java
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);
    }
}

LambdaVsReferensi.java

Unduh berkas
java
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));
        }
    }
}