Ich möchte in der Lage sein, eine Funktion wie:
class A {
private String extraVar;
public String myFormat(String format, Object ... args){
return String.format(format, extraVar, args);
}
}
Das Problem hierbei ist, dass args
in der Methode myFormat
als Object[]
Behandelt wird und somit ein einziges Argument für String.format
Ist, während ich jeden haben möchte single Object
in args
, das als neues Argument übergeben werden soll. Da String.format
Auch eine Methode mit variablen Argumenten ist, 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 extraVar
mit einem neuen Array args
voranstellen und an diese Methode übergeben.
Der zugrunde liegende Typ einer variadischen Methode function(Object... args)
isfunction(Object[] args)
. Sun fügte auf diese Weise Varargs hinzu, um die Abwärtskompatibilität zu erhalten.
Sie sollten also nur in der Lage sein, extraVar
vor args
zu stellen und String.format(format, args)
aufzurufen.
Ja, ein T...
Ist nur ein syntaktischer Zucker für einen T[]
.
Der letzte formale Parameter in einer Liste ist etwas Besonderes. Es kann sich um einen Parameter mit variabler Arität handeln, der durch ein Elipsenzeichen nach dem Typ angezeigt wird.
Wenn der letzte formale Parameter ein variabler Aritätsparameter vom Typ
T
ist, wird davon ausgegangen, dass er einen formalen Parameter vom TypT[]
Definiert. Die Methode ist dann eine Methode mit variabler Arität . Andernfalls 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 Parameter variable arity 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 Methode main
ist gültig, da String...
Wiederum nur String[]
Ist. Da Arrays kovariant sind, ist ein String[]
Ein Object[]
, Sodass Sie auch ezFormat(args)
aufrufen können.
null
übergebenWie Varargs gelöst werden, ist ziemlich kompliziert, und manchmal tut 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 der Art und Weise, wie Varargs aufgelöst werden, wird die letzte Anweisung mit objs = null
Aufgerufen, was natürlich mit objs.length
Zu NullPointerException
führen würde. Wenn Sie einem varargs-Parameter ein Argument null
geben möchten, haben Sie folgende Möglichkeiten:
count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"
Das Folgende ist ein Beispiel für einige der Fragen, die Menschen im Umgang mit Varg gestellt haben:
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 erhält ezFormat
zwei Argumente, von denen das erste ein String[]
Und das zweite ein String
ist. Wenn Sie ein Array an varargs übergeben und möchten, dass seine Elemente als einzelne Argumente erkannt werden, und Sie müssen auch ein zusätzliches Argument hinzufügen, müssen Sie ein anderes erstellen Array , das das zusätzliche Element aufnimmt.
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 ]"
Es "funktioniert" nicht:
int[] myNumbers = { 1, 2, 3 };
System.out.println(ezFormat(myNumbers));
// prints "[ [[email protected] ]"
Varargs funktioniert nur mit Referenztypen. Autoboxing gilt nicht für Arrays von Primitiven. Die folgenden Arbeiten:
Integer[] myNumbers = { 1, 2, 3 };
System.out.println(ezFormat(myNumbers));
// prints "[ 1 ][ 2 ][ 3 ]"
Es ist in Ordnung, ein Array zu übergeben - tatsächlich läuft es auf dasselbe hinaus
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 Parameter vararg erwartet.
Sehen
jasonmp85 hat Recht, ein anderes Array an String.format
zu übergeben. Die Größe eines Arrays kann nach seiner Erstellung 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);
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.