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.
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:
Tanpa Judul (Rp0)
Algoritma Dasar (Rp0)
Belajar Java (Rp95000)
Rak berisi 3 slot, slot pertama: PemrogramanMari kita bedah berkas tersebut baris demi baris:
- Baris 8-27 (class
Bukudengan tiga constructor): class ini sengaja memiliki tiga overload — tanpa argumen, satu argumenjudul, dan dua argumenjudul+harga. Ketiganya punya tubuh berbeda sehingga keluaran program membuktikan constructor mana yang benar-benar dijalankan. - Baris 40 (
Supplier<Buku> pabrikKosong = Buku::new;):get()tidak menerima parameter, jadi compiler memilih constructor tanpa argumen. Hasil cetaknyaTanpa Judul (Rp0)— buktiBuku()yang dijalankan. - Baris 44 (
Function<String, Buku> pabrikJudul = Buku::new;):apply(String)menerima satuString, jadi compiler memilihBuku(String). Inilah overload resolution bekerja untuk constructor reference — tandaBuku::newyang sama membawa tiga makna berbeda, dipilih dari target type. - Baris 48 (
BiFunction<String, Integer, Buku> pabrikLengkap = Buku::new;): dua parameter (String,Integer) memilihBuku(String, int);Integerdi-unboxing menjadiintsecara otomatis. - Baris 52-55 (
IntFunction<String[]> pabrikRak = String[]::new;): bentuk khusus untuk pembuatan array.apply(3)menjalankannew String[3]— angka pada parameter menjadi panjang array, bukan isi elemennya. Elemen diisi setelahnya lewatrak[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:
Jumlah elemen array : 4
Isi TreeSet : [budi, dewi, siti]Dua pengamatan dari program tersebut:
- Baris 10 (
nama.toArray(String[]::new)): methodtoArraymeminta sebuah “resep” cara membuat array hasil.String[]::newmemberi resepnew String[n]denganndihitung dari ukuran collection — kita tidak perlu membuat array kosong sendiri dan tidak perlu memikirkan ukurannya. - Baris 14-15 (
Collectors.toCollection(TreeSet::new)): Stream API bisa mengumpulkan hasil keArrayList,HashSet, atau koleksi apa pun — yang dibutuhkan hanyalahSupplier<C>.TreeSet::newmengarahkan hasil keTreeSetsehingga 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 #
- Oracle: java.util.function (Package Summary)
- dev.java: Lambda Expressions
- Oracle: java.util.stream.Collectors
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
PabrikBuku.java
Unduh berkasimport 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 berkasimport 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);
}
}