Kann ich ein Array als Argumente an eine Methode mit variablen Argumenten in Java übergeben?

275

Ich möchte eine Funktion erstellen können wie:

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

Das Problem hierbei ist , dass argsso behandelt wird , Object[]in dem Verfahren myFormat, und ist somit ein einziges Argument String.format, während ich jedes einzelne möchte Objectin argsals neues Argument übergeben werden. Da String.formates sich auch um eine Methode mit variablen Argumenten handelt, sollte dies möglich sein.

Wenn dies nicht möglich ist, gibt es eine Methode wie String.format(String format, Object[] args)? In diesem Fall könnte ich extraVarder argsVerwendung eines neuen Arrays voranstellen und es an diese Methode übergeben.

user362382
quelle
8
Ich kann nicht anders, als mich zu fragen, warum diese Frage eine "ist diese gültige" Art von Frage ist. Könnten Sie es nicht einfach versucht haben? Übertreibe nicht zu fragen, du wirst dir mehr schaden als nützen.
Kamasheto
21
Das hätte leicht getestet werden können. Das Schöne an einer solchen Frage ist jedoch, dass sie das Thema aufdeckt und interessante Antworten einholt.
Akf
2
Ich habe den obigen Code tatsächlich ausprobiert, mit der Absicht, die Array-Argumente als Argumente an die Methode zu übergeben. Ich wusste jedoch nicht, dass ich extraVar zuerst vor Argumente hätte stellen sollen. Wenn Sie wissen, dass variable Argumente als Array behandelt werden (auch außerhalb der Methode), ist dies natürlich ziemlich logisch.
user362382

Antworten:

180

Der zugrunde liegende Typ einer variadischen Methode function(Object... args) ist function(Object[] args) . Sun fügte auf diese Weise Varargs hinzu, um die Abwärtskompatibilität zu gewährleisten.

So sollten Sie nur in der Lage sein , vorangestellt extraVarzu argsund Anruf String.format(format, args).

jasonmp85
quelle
Wenn Sie ein Argument vom Typ X[]an eine Methode übergeben, x(X... xs)wird in Eclipse die folgende Warnung ausgegeben: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

Ja, a T...ist nur ein syntaktischer Zucker für a T[].

JLS 8.4.1 Formatparameter

Der letzte formale Parameter in einer Liste ist speziell. Es kann sich um einen variablen Aritätsparameter handeln , der durch eine Elipse nach dem Typ angezeigt wird.

Wenn der letzte formale Parameter ein variabler Aritätsparameter vom Typ ist T, wird davon ausgegangen, dass er einen formalen Parameter vom Typ definiert T[]. Die Methode ist dann eine Methode mit variabler Arität . Ansonsten handelt es sich um eine Methode mit fester Arität . Aufrufe einer variablen Aritätsmethode können mehr tatsächliche Argumentausdrücke als formale Parameter enthalten. Alle tatsächlichen Argumentausdrücke, die nicht den formalen Parametern vor dem Variablenaritätsparameter entsprechen, werden ausgewertet und die Ergebnisse in einem Array gespeichert, das an den Methodenaufruf übergeben wird.

Hier ist ein Beispiel zur Veranschaulichung:

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 ]"
}

Und ja, die obige mainMethode ist gültig, weil sie wieder String...gerecht ist String[]. Da Arrays kovariant sind, String[]ist a auch ein Object[], sodass Sie auch in ezFormat(args)beide Richtungen aufrufen können.

Siehe auch


Varargs Fallstricke # 1: vorbei null

Wie Varargs gelöst werden, ist ziemlich kompliziert und manchmal macht es Dinge, die Sie überraschen können.

Betrachten Sie dieses Beispiel:

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!!!

Aufgrund wie varargs aufgelöst werden, die letzte Anweisung ruft mit objs = null, was natürlich dazu führen würde , NullPointerExceptionmit objs.length. Wenn Sie nulleinem varargs-Parameter ein Argument geben möchten, haben Sie folgende Möglichkeiten:

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

Verwandte Fragen

Das Folgende ist ein Beispiel für einige der Fragen, die Menschen beim Umgang mit Varargs gestellt haben:


Vararg Fallstricke # 2: Hinzufügen zusätzlicher Argumente

Wie Sie herausgefunden haben, "funktioniert" Folgendes nicht:

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

Aufgrund der Funktionsweise von varargs werden ezFormattatsächlich zwei Argumente angezeigt , von denen das erste a String[]und das zweite a ist String. Wenn Sie ein Array an varargs übergeben und möchten, dass seine Elemente als einzelne Argumente erkannt werden, und Sie auch ein zusätzliches Argument hinzufügen müssen, haben Sie keine andere Wahl, als ein anderes Array zu erstellen , das das zusätzliche Element enthält.

Hier sind einige nützliche Hilfsmethoden:

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;
}

Jetzt können Sie Folgendes tun:

    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 Fallstricke # 3: Übergeben einer Reihe von Grundelementen

Es "funktioniert" nicht:

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

Varargs funktioniert nur mit Referenztypen. Autoboxing gilt nicht für eine Reihe von Grundelementen. Folgendes funktioniert:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
Polygenschmierstoffe
quelle
2
Die Verwendung von variablen Parametern für Object ... erschwert die Verwendung zusätzlicher Signaturen, da der Compiler Mehrdeutigkeiten zwischen den Signaturen erkennt, bei denen sogar ein primitives Argument automatisch an Object gesendet werden kann.
Aal Gheez
7
Es fehlt ein Gotcha: Wenn Ihre Methode einen letzten Parameter vom Typ Object hat ... und Sie ihn aufrufen, indem Sie beispielsweise einen String [] übergeben. Der Compiler weiß nicht, ob Ihr Array das erste Element der Varargs oder das Äquivalent aller Varargs ist. Es wird dich warnen.
Snicolas
Wenn Sie ein Argument vom Typ X[]an eine Methode übergeben, x(X... xs)wird in Eclipse die folgende Warnung ausgegeben: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

Es ist in Ordnung, ein Array zu übergeben - tatsächlich ist es dasselbe

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

ist das gleiche wie

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

Es ist nur syntaktischer Zucker - der Compiler konvertiert den ersten in den zweiten, da die zugrunde liegende Methode ein Array für den vararg- Parameter erwartet .

Sehen

mdma
quelle
4

jasonmp85 hat Recht, ein anderes Array an zu übergeben String.format. Die Größe eines Arrays kann nach dem Erstellen nicht mehr geändert werden. Sie müssen also ein neues Array übergeben, anstatt das vorhandene zu ändern.

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

Ich hatte das gleiche Problem.

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

Und dann das obj als varargs Argument übergeben. Es funktionierte.

Srijit Paul
quelle
2
Bitte fügen Sie das auch hinzu.
Mathews Sunny