Bisakah saya meneruskan array sebagai argumen ke metode dengan argumen variabel di Jawa?

276

Saya ingin dapat membuat fungsi seperti:

class A {
  private String extraVar;
  public String myFormat(String format, Object ... args){
    return String.format(format, extraVar, args);
  }
}

Masalahnya di sini adalah bahwa argsdiperlakukan sebagai Object[]dalam metode myFormat, dan dengan demikian merupakan satu argumen untuk String.format, sementara saya ingin setiap satu Objectdi argsakan disahkan sebagai argumen baru. Karena String.formatjuga merupakan metode dengan argumen variabel, ini harus dimungkinkan.

Jika ini tidak mungkin, apakah ada metode seperti String.format(String format, Object[] args)? Dalam hal ini saya bisa tambahkan extraVaruntuk argsmenggunakan array baru dan menyebarkannya ke metode tersebut.

pengguna362382
sumber
8
Saya bertanya-tanya mengapa pertanyaan ini adalah jenis pertanyaan "apakah ini valid". Tidak bisakah Anda mencobanya? Jangan berlebihan bertanya, Anda akan merugikan diri sendiri lebih daripada kebaikan.
kamasheto
21
cukup benar, ini bisa dengan mudah diuji. Namun, hal yang menyenangkan tentang pertanyaan seperti ini adalah bahwa ia memaparkan topik dan meminta jawaban yang menarik.
akf
2
Saya benar-benar mencoba kode di atas, dengan maksud untuk melewatkan array args sebagai argumen ke metode. Namun, saya tidak menyadari bahwa saya harus menambahkan extraVar ke args terlebih dahulu. Ketika Anda tahu bahwa argumen variabel diperlakukan sebagai array (bahkan di luar metode), ini tentu saja cukup logis.
user362382

Jawaban:

181

Jenis metode variadic yang mendasarinya function(Object... args) adalah function(Object[] args) . Sun menambahkan vararg dengan cara ini untuk menjaga kompatibilitas.

Jadi Anda hanya harus dapat tambahkan extraVarke argsdan panggilan String.format(format, args).

jasonmp85
sumber
1
Melewati argumen tipe X[]ke dalam metode x(X... xs)memberikan peringatan berikut dalam Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Luke Hutchison
318

Ya, a T...hanya gula sintaksis untuk a T[].

JLS 8.4.1 Format parameter

Parameter formal terakhir dalam daftar adalah khusus; mungkin parameter arity variabel , ditunjukkan oleh elipsis mengikuti tipe.

Jika parameter formal terakhir adalah parameter arity variabel dari tipe T, itu dianggap untuk mendefinisikan parameter formal tipe T[]. Metode ini kemudian menjadi metode variabel arity . Kalau tidak, itu adalah metode arity tetap . Doa metode arity variabel dapat berisi lebih banyak ekspresi argumen aktual daripada parameter formal. Semua ekspresi argumen aktual yang tidak sesuai dengan parameter formal sebelum parameter variabel arity akan dievaluasi dan hasilnya disimpan ke dalam array yang akan diteruskan ke pemanggilan metode.

Berikut ini contoh untuk diilustrasikan:

public static String ezFormat(Object... args) {
    String format = new String(new char[args.length])
        .replace("\0", "[ %s ]");
    return String.format(format, args);
}
public static void main(String... args) {
    System.out.println(ezFormat("A", "B", "C"));
    // prints "[ A ][ B ][ C ]"
}

Dan ya, mainmetode di atas valid, karena sekali lagi, String...adil String[]. Juga, karena array adalah kovarian, a String[]adalah Object[], jadi Anda juga dapat memanggil ezFormat(args)keduanya.

Lihat juga


Varargs mengerti # 1: passing null

Cara vararg diselesaikan cukup rumit, dan terkadang melakukan hal-hal yang mungkin mengejutkan Anda.

Pertimbangkan contoh ini:

static void count(Object... objs) {
    System.out.println(objs.length);
}

count(null, null, null); // prints "3"
count(null, null); // prints "2"
count(null); // throws java.lang.NullPointerException!!!

Karena bagaimana varargs diselesaikan, pernyataan memanggil terakhir dengan objs = null, yang tentu saja akan menyebabkan NullPointerExceptiondengan objs.length. Jika Anda ingin memberikan satu nullargumen ke parameter varargs, Anda dapat melakukan salah satu dari berikut ini:

count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"

Pertanyaan-pertanyaan Terkait

Berikut ini adalah contoh dari beberapa pertanyaan yang diajukan orang ketika berurusan dengan varargs:


Vararg Gotchas # 2: menambahkan argumen tambahan

Seperti yang Anda ketahui, yang berikut ini tidak "bekerja":

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(myArgs, "Z"));
    // prints "[ [Ljava.lang.String;@13c5982 ][ Z ]"

Karena cara kerja varargs, ezFormatsebenarnya ada 2 argumen, yang pertama adalah a String[], yang kedua adalah a String. Jika Anda meneruskan array ke varargs, dan Anda ingin elemen-elemennya dikenali sebagai argumen individual, dan Anda juga perlu menambahkan argumen tambahan, maka Anda tidak punya pilihan selain membuat array lain yang mengakomodasi elemen tambahan.

Berikut adalah beberapa metode penolong yang berguna:

static <T> T[] append(T[] arr, T lastElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    arr[N] = lastElement;
    return arr;
}
static <T> T[] prepend(T[] arr, T firstElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    System.arraycopy(arr, 0, arr, 1, N);
    arr[0] = firstElement;
    return arr;
}

Sekarang Anda dapat melakukan hal berikut:

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(append(myArgs, "Z")));
    // prints "[ A ][ B ][ C ][ Z ]"

    System.out.println(ezFormat(prepend(myArgs, "Z")));
    // prints "[ Z ][ A ][ B ][ C ]"

Varargs gotchas # 3: melewati sebuah array primitif

Itu tidak "bekerja":

    int[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ [I@13c5982 ]"

Varargs hanya berfungsi dengan tipe referensi. Autoboxing tidak berlaku untuk array primitif. Karya-karya berikut:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
polygenelubricants
sumber
2
Menggunakan Object ... parameter variad membuatnya sulit untuk menggunakan tanda tangan tambahan karena kompiler mengidentifikasi ambiguitas antara tanda tangan di mana bahkan argumen primitif dapat diobobox ke Objek.
belut ghEEz
7
Ada gotcha yang hilang: jika metode Anda memiliki param terakhir dari tipe Object ... dan Anda menyebutnya lewat String [] misalnya. Kompiler tidak tahu apakah array Anda adalah item pertama dari varargs atau setara dengan semua varargs. Ini akan memperingatkan Anda.
Snicolas
Melewati argumen tipe X[]ke dalam metode x(X... xs)memberikan peringatan berikut dalam Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Luke Hutchison
23

Tidak apa-apa untuk melewatkan array - pada kenyataannya itu sama dengan hal yang sama

String.format("%s %s", "hello", "world!");

sama dengan

String.format("%s %s", new Object[] { "hello", "world!"});

Ini hanya gula sintaksis - kompiler mengubah yang pertama menjadi yang kedua, karena metode yang mendasarinya mengharapkan array untuk parameter vararg .

Lihat

mdma
sumber
4

jasonmp85 benar tentang meneruskan array yang berbeda String.format. Ukuran array tidak dapat diubah begitu dibangun, jadi Anda harus melewati array baru alih-alih memodifikasi yang sudah ada.

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);
ebelisle
sumber
1

Saya mengalami masalah yang sama.

String[] arr= new String[] { "A", "B", "C" };
Object obj = arr;

Dan kemudian melewati obj sebagai argumen varargs. Itu berhasil.

Srijit Paul
sumber
2
Tolong tambahkan juga.
Mathews Sunny