Method reference punya satu anggota keluarga yang bekerja sedikit berbeda: constructor reference, ditulis NamaClass::new. Alih-alih merujuk method yang berjalan pada objek, referensi ini merujuk pabrik pembuat objek — setiap kali method abstraknya dipanggil, sebuah objek baru dibuat. Kita sudah melihat sekilas ArrayList::new pada Chapter Method References; sekarang kita bedah tuntas bagaimana compiler javac memilih constructor yang tepat, termasuk saat class punya beberapa overload, dan bagaimana pustaka standar memanfaatkan bentuk ini.

Memetakan Constructor ke Functional Interface #

Constructor reference tidak punya bentuk SAM khusus — ia menempel pada functional interface apa pun yang bentuk parameternya cocok. Aturan mainnya: daftar parameter method abstrak menentukan constructor mana yang dipanggil, dan tipe kembalian method abstrak harus menjadi tipe objek yang dibuat. Constructor tanpa argumen cocok dengan Supplier<T>; constructor satu argumen cocok dengan Function<A, T>; constructor dua argumen cocok dengan BiFunction<A, B, T> — dan pola ini berlanjut untuk class Function3 ke atas yang hanya tersedia lewat pustaka pihak ketiga.

java PabrikBuku.java
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;

class Buku {
    String judul;
    int harga;

    Buku() {
        this.judul = "Tanpa Judul";
        this.harga = 0;
    }

    Buku(String judul) {
        this.judul = judul;
        this.harga = 0;
    }

    Buku(String judul, int harga) {
        this.judul = judul;
        this.harga = harga;
    }

    @Override
    public String toString() {
        return judul + " (Rp" + harga + ")";
    }
}

public class PabrikBuku {
    public static void main(String[] args) {
        // Constructor tanpa argumen dipetakan ke Supplier.get()
        Supplier<Buku> pabrikKosong = Buku::new;
        System.out.println(pabrikKosong.get());

        // Constructor satu argumen dipetakan ke Function.apply()
        Function<String, Buku> pabrikJudul = Buku::new;
        System.out.println(pabrikJudul.apply("Algoritma Dasar"));

        // Constructor dua argumen dipetakan ke BiFunction.apply()
        BiFunction<String, Integer, Buku> pabrikLengkap = Buku::new;
        System.out.println(pabrikLengkap.apply("Belajar Java", 95000));

        // Constructor array dipetakan ke IntFunction.apply(int)
        IntFunction<String[]> pabrikRak = String[]::new;
        String[] rak = pabrikRak.apply(3);
        rak[0] = "Pemrograman";
        System.out.println("Rak berisi " + rak.length + " slot, slot pertama: " + rak[0]);
    }
}

Output yang Dihasilkan:

text
Tanpa Judul (Rp0)
Algoritma Dasar (Rp0)
Belajar Java (Rp95000)
Rak berisi 3 slot, slot pertama: Pemrograman

Mari kita bedah berkas tersebut baris demi baris:

  1. Baris 8-27 (class Buku dengan tiga constructor): class ini sengaja memiliki tiga overload — tanpa argumen, satu argumen judul, dan dua argumen judul+harga. Ketiganya punya tubuh berbeda sehingga keluaran program membuktikan constructor mana yang benar-benar dijalankan.
  2. Baris 40 (Supplier<Buku> pabrikKosong = Buku::new;): get() tidak menerima parameter, jadi compiler memilih constructor tanpa argumen. Hasil cetaknya Tanpa Judul (Rp0) — bukti Buku() yang dijalankan.
  3. Baris 44 (Function<String, Buku> pabrikJudul = Buku::new;): apply(String) menerima satu String, jadi compiler memilih Buku(String). Inilah overload resolution bekerja untuk constructor reference — tanda Buku::new yang sama membawa tiga makna berbeda, dipilih dari target type.
  4. Baris 48 (BiFunction<String, Integer, Buku> pabrikLengkap = Buku::new;): dua parameter (String, Integer) memilih Buku(String, int); Integer di-unboxing menjadi int secara otomatis.
  5. Baris 52-55 (IntFunction<String[]> pabrikRak = String[]::new;): bentuk khusus untuk pembuatan array. apply(3) menjalankan new String[3] — angka pada parameter menjadi panjang array, bukan isi elemennya. Elemen diisi setelahnya lewat rak[0] = "Pemrograman".

Constructor Reference di API Pustaka Standar #

Kekuatan sesungguhannya muncul saat constructor reference disuntikkan ke method pustaka sebagai strategi pembuatan koleksi. Dua contoh klasik: toArray(IntFunction) pada collection, dan Collectors.toCollection(Supplier) pada Stream API:

import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class PabrikKoleksi {
    public static void main(String[] args) {
        List<String> nama = List.of("siti", "budi", "dewi", "budi");

        // Constructor reference array sebagai ukuran koleksi hasil
        String[] salinan = nama.toArray(String[]::new);
        System.out.println("Jumlah elemen array : " + salinan.length);

        // Constructor reference sebagai pabrik koleksi pada Stream API
        TreeSet<String> unik = nama.stream()
                .collect(Collectors.toCollection(TreeSet::new));
        System.out.println("Isi TreeSet         : " + unik);
    }
}

Output yang Dihasilkan:

text
Jumlah elemen array : 4
Isi TreeSet         : [budi, dewi, siti]

Dua pengamatan dari program tersebut:

  1. Baris 10 (nama.toArray(String[]::new)): method toArray meminta sebuah “resep” cara membuat array hasil. String[]::new memberi resep new String[n] dengan n dihitung dari ukuran collection — kita tidak perlu membuat array kosong sendiri dan tidak perlu memikirkan ukurannya.
  2. Baris 14-15 (Collectors.toCollection(TreeSet::new)): Stream API bisa mengumpulkan hasil ke ArrayList, HashSet, atau koleksi apa pun — yang dibutuhkan hanyalah Supplier<C>. TreeSet::new mengarahkan hasil ke TreeSet sehingga otomatis terurut dan duplikat "budi" menyusut menjadi satu: keluaran [budi, dewi, siti].

Contoh TreeSet ini juga memperlihatkan alasan pola fungsional terasa ringan: satu token TreeSet::new mengubah seluruh perilaku pengumpulan data tanpa menulis class pembungkus atau blok konfigurasi.

Kapan Constructor Reference Layak Dipakai #

Dua situasi membuat constructor reference pilihan terbaik. Pertama, saat API meminta supplier secara eksplisit — toArray, toCollection, Supplier<T> parameter — karena bentuknya memang dirancang untuk itu. Kedua, saat membuat factory kecil di kode sendiri: method buatLaporan(Supplier<Format> format) menerima JsonFormat::new atau CsvFormat::new dan pembaca kode langsung melihat pilihan tipenya. Sebaliknya, bila objek hanya dibuat sekali di satu tempat, new Buku("Judul", 5000) biasa tetap paling jernih — referensi ::new unggul justru ketika pembuatan objek dijadikan nilai yang berpindah-pindah.

Factory berbasis ::new menutup rangkaian cara menulis behavior: lambda untuk logika baru, method reference untuk method yang ada, constructor reference untuk pembuatan objek. Berikutnya kita menata semua rumah yang bisa ditempati behavior tersebut: paket java.util.function beserta katalog lengkap interface bawaannya.

Referensi #

Berkas Sumber

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

PabrikBuku.java

Unduh berkas
java
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;

class Buku {
    String judul;
    int harga;

    Buku() {
        this.judul = "Tanpa Judul";
        this.harga = 0;
    }

    Buku(String judul) {
        this.judul = judul;
        this.harga = 0;
    }

    Buku(String judul, int harga) {
        this.judul = judul;
        this.harga = harga;
    }

    @Override
    public String toString() {
        return judul + " (Rp" + harga + ")";
    }
}

public class PabrikBuku {
    public static void main(String[] args) {
        // Constructor tanpa argumen dipetakan ke Supplier.get()
        Supplier<Buku> pabrikKosong = Buku::new;
        System.out.println(pabrikKosong.get());

        // Constructor satu argumen dipetakan ke Function.apply()
        Function<String, Buku> pabrikJudul = Buku::new;
        System.out.println(pabrikJudul.apply("Algoritma Dasar"));

        // Constructor dua argumen dipetakan ke BiFunction.apply()
        BiFunction<String, Integer, Buku> pabrikLengkap = Buku::new;
        System.out.println(pabrikLengkap.apply("Belajar Java", 95000));

        // Constructor array dipetakan ke IntFunction.apply(int)
        IntFunction<String[]> pabrikRak = String[]::new;
        String[] rak = pabrikRak.apply(3);
        rak[0] = "Pemrograman";
        System.out.println("Rak berisi " + rak.length + " slot, slot pertama: " + rak[0]);
    }
}

PabrikKoleksi.java

Unduh berkas
java
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class PabrikKoleksi {
    public static void main(String[] args) {
        List<String> nama = List.of("siti", "budi", "dewi", "budi");

        // Constructor reference array sebagai ukuran koleksi hasil
        String[] salinan = nama.toArray(String[]::new);
        System.out.println("Jumlah elemen array : " + salinan.length);

        // Constructor reference sebagai pabrik koleksi pada Stream API
        TreeSet<String> unik = nama.stream()
                .collect(Collectors.toCollection(TreeSet::new));
        System.out.println("Isi TreeSet         : " + unik);
    }
}