Varargs

Menerima jumlah argumen yang bebas dengan int... nums dan memahami penurunannya menjadi array.

Daftar isi

Jumlah argumen sebuah metode biasanya tetap sejak deklarasi, tetapi ada kasus yang menuntut kebebasan: menjumlahkan sekelompok angka, menggabungkan beberapa teks, atau menghitung rata-rata nilai yang jumlahnya baru diketahui saat pemanggilan. Varargs menjawab kebutuhan itu dengan satu parameter yang menerima sejumlah argumen sekaligus. Topik ini membahas sintaksnya, wujud sebenarnya di dalam metode, cara pemanggilannya, dan jebakan yang menyertainya.

Sintaks varargs#

Parameter varargs ditulis dengan tiga titik setelah tipenya, misalnya double... angka. Dari seluruh parameter sebuah metode, varargs hanya boleh ada satu dan wajib berada di posisi terakhir. Aturan ini lahir dari cara kompilator membaca pemanggilan, yaitu ia harus tahu batas pasti antara parameter tetap dan argumen yang menumpuk di belakangnya.

java Contoh: penempatan varargs yang ditolak
// tidak-dijalankan
static void salah(int... angka, String label) { }   // varargs wajib paling akhir
static void salahJuga(int... a, int... b) { }       // hanya satu parameter varargs

Bila aturan itu dilanggar, kompilator berhenti dengan kesalahan pada baris deklarasinya, bukan pada pemanggilannya. Bentuk yang sah memisahkan parameter tetap di depan lalu menaruh varargs di ujung, misalnya gabung(String pemisah, String... teks). Dengan bentuk itu, argumen pertama selalu jadi pemisah, sedangkan sisanya dikumpulkan menjadi satu larik teks.

Di dalam metode, varargs adalah array#

Kompilator menurunkan setiap parameter varargs menjadi parameter array bertipe sama. Di dalam tubuh metode, angka bertipe double[] dan dipakai seperti array biasa, yaitu dibaca length-nya, diindeks, atau dilalui dengan for-each.

java RingkasanAngka.java
public class RingkasanAngka {

    static double total(double... angka) {
        double jumlah = 0;
        for (double a : angka) {
            jumlah += a;
        }
        return jumlah;
    }

    static double rataRata(double... angka) {
        return total(angka) / angka.length;
    }

    public static void main(String[] args) {
        System.out.println("total(2, 3) = " + total(2, 3));
        System.out.println("total(1, 2, 3, 4) = " + total(1, 2, 3, 4));
        System.out.println("rataRata(8, 9, 10) = " + rataRata(8, 9, 10));
        System.out.println("rataRata() = " + rataRata());
    }
}
text Keluaran
total(2, 3) = 5.0
total(1, 2, 3, 4) = 10.0
rataRata(8, 9, 10) = 9.0
rataRata() = NaN

Pemanggilan total(2, 3) membuat kompilator menyusun array baru berisi 2.0 dan 3.0, lalu menyerahkannya ke metode. Pemanggilan rataRata() tanpa satu argumen pun tetap sah, dan parameternya menjadi array kosong dengan length bernilai 0. Karena pembilangnya 0.0 dan penyebutnya 0 pada aritmetika double, hasilnya NaN.

Memanggil dengan array atau tanpa argumen#

Karena varargs adalah array, pemanggil boleh mengirim array yang sudah jadi sebagai ganti daftar argumen lepas. Sebaliknya tidak berlaku: parameter bertipe array wajib menerima array dan tidak bisa dipanggil dengan argumen terpisah.

java PadananArray.java
public class PadananArray {

    static int totalVarargs(int... angka) {
        return jumlahkan(angka);
    }

    static int totalArray(int[] angka) {
        return jumlahkan(angka);
    }

    static int jumlahkan(int[] angka) {
        int jumlah = 0;
        for (int a : angka) {
            jumlah += a;
        }
        return jumlah;
    }

    public static void main(String[] args) {
        System.out.println(totalVarargs(1, 2, 3));
        System.out.println(totalVarargs(new int[] {4, 5}));
        System.out.println(totalArray(new int[] {6, 7, 8}));
    }
}
text Keluaran
6
9
21

Ketiga pemanggilan menghasilkan total yang benar melalui jalur berbeda. totalVarargs(1, 2, 3) memakai argumen lepas, totalVarargs(new int[] {4, 5}) mengirim array eksplisit, dan totalArray(new int[] {6, 7, 8}) memakai parameter array biasa. Satu perbedaan penting untuk diingat: setiap pemanggilan lepas membangun array baru, sedangkan array yang dikirim eksplisit diteruskan tanpa disalin. Mengubah isinya di dalam metode berarti mengubah array milik pemanggil, karena yang dikirim adalah referensinya.

Varargs di pustaka bawaan#

Banyak API bawaan memakai varargs, sehingga pola ini terasa pada pemakaian sehari-hari. String.format menyusun teks dari satu String pola ditambah sejumlah isian, System.out.printf menerima format dengan argumen yang jumlahnya bebas, dan List.of membungkus sejumlah elemen menjadi list. Ketiganya meletakkan parameter tetap di depan dan varargs di akhir, persis seperti bentuk sah yang dibahas di atas.

Metode main juga menerima varargs. Deklarasi public static void main(String... args) sah dan berperilaku sama dengan main(String[] args), karena di dalamnya args tetap array. Perbedaannya muncul saat metode itu dipanggil dari kode lain. Versi varargs bisa dipanggil dengan argumen terpisah, sedangkan versi array menuntut array eksplisit sebagai satu-satunya bentuk argumennya.

Kesalahan umum#

  • Menaruh varargs di tengah daftar parameter atau memasang dua varargs sekaligus. Deklarasi ditolak kompilator. Pasang satu varargs di posisi terakhir.
  • Mengirim null sebagai satu-satunya argumen. Kompilator menerimanya sebagai referensi array yang null, lalu pembacaan length di dalam metode melempar NullPointerException. Kirim array kosong sebagai gantinya.
  • Mendeklarasikan proses(int[] angka) dan proses(int... angka) di kelas yang sama. Keduanya memiliki signature identik setelah varargs diturunkan menjadi array, sehingga kompilasi ditolak.
  • Memakai varargs pada metode yang hampir selalu dipanggil dengan jumlah argumen tetap. Setiap pemanggilan lepas menyusun array baru, sehingga overload dengan jumlah parameter tetap lebih murah untuk kasus tersebut.

Ringkasan#

  • Varargs ditulis dengan tiga titik setelah tipe, hanya boleh satu, dan wajib menjadi parameter terakhir.
  • Di dalam metode, parameter varargs adalah array biasa yang bisa saja kosong.
  • Pemanggilan lepas menyusun array baru, sedangkan pengiriman array eksplisit meneruskan referensinya tanpa salinan.
  • Banyak API bawaan seperti String.format dan List.of memakai pola parameter tetap di depan ditambah varargs.

Referensi#