web-dev-qa-db-fra.com

Puis-je passer un tableau en tant qu'arguments à une méthode avec des arguments variables en Java?

J'aimerais pouvoir créer une fonction comme:

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

Le problème ici est que args est traité comme Object[] dans la méthode myFormat, et constitue donc un argument unique pour String.format, alors que je voudrais chaque Object dans args à transmettre en tant que nouvel argument. Puisque String.format est également une méthode avec des arguments variables, cela devrait être possible.

Si ce n'est pas possible, existe-t-il une méthode comme String.format(String format, Object[] args)? Dans ce cas, je pourrais ajouter extraVar à args en utilisant un nouveau tableau et le transmettre à cette méthode.

249
user362382

Le type sous-jacent d'une méthode variadique function(Object... args)estfunction(Object[] args). Sun a ajouté varargs de cette manière afin de préserver la compatibilité avec les versions antérieures.

Vous devriez donc pouvoir ajouter extraVar à args et appeler String.format(format, args).

168
jasonmp85

Oui, un T... n'est qu'un sucre syntaxique pour un T[].

paramètres de format JLS 8.4.1

Le dernier paramètre formel d'une liste est spécial; il peut s'agir d'un paramètre variable d'arity , indiqué par un signe suivant le type.

Si le dernier paramètre formel est un paramètre d'arity variable de type T, il est envisagé de définir un paramètre formel de type T[]. La méthode est alors une méthode à arité variable . Sinon, il s'agit d'une méthode arité fixe . Les invocations d'une méthode d'arity variable peuvent contenir plus d'expressions d'arguments réelles que de paramètres formels. Toutes les expressions d'arguments réelles qui ne correspondent pas aux paramètres formels précédant le paramètre d'arity variable seront évaluées et les résultats stockés dans un tableau qui sera transmis à l'appel de la méthode.

Voici un exemple pour illustrer:

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

Et oui, la méthode main ci-dessus est valide, car encore une fois, String... n'est que String[]. De plus, comme les tableaux sont covariants, un String[] est un Object[], de sorte que vous pouvez également appeler ezFormat(args) dans les deux sens.

Voir également


Varargs piège # 1: passe null

La façon dont les varargs sont résolus est assez compliquée, et parfois elle fait des choses qui peuvent vous surprendre.

Considérons cet exemple:

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

En raison de la manière dont les variables sont résolues, la dernière instruction appelle avec objs = null, ce qui entraînerait bien sûr NullPointerException avec objs.length. Si vous souhaitez attribuer un argument null à un paramètre varargs, vous pouvez effectuer l'une des opérations suivantes:

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

Questions connexes

Voici un exemple de certaines des questions que les gens ont posées lorsqu'ils traitent de varargs:


Vararg piège # 2: ajout d'arguments supplémentaires

Comme vous l'avez découvert, ce qui suit ne fonctionne pas:

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

En raison du fonctionnement de varargs, ezFormat obtient en fait 2 arguments, le premier étant un String[], le second étant un String. Si vous passez un tableau à varargs et que vous voulez que ses éléments soient reconnus en tant qu'arguments individuels et que vous ayez également besoin d'ajouter un argument supplémentaire, vous n'avez pas d'autre choix que de créer un autre tableau qui accueille l'élément supplémentaire.

Voici quelques méthodes utiles d'aide:

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

Maintenant, vous pouvez faire ce qui suit:

    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: passer un tableau de primitives

Cela ne fonctionne pas:

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

Varargs ne fonctionne qu'avec les types de référence. La sélection automatique ne s'applique pas à un tableau de primitives. Les oeuvres suivantes:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
298

C'est ok pour passer un tableau - en fait c'est la même chose

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

est le même que

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

C'est juste du sucre syntaxique - le compilateur convertit le premier en second, car la méthode sous-jacente attend un tableau pour le paramètre vararg .

Voir

21
mdma

jasonmp85 a raison de passer un tableau différent à String.format. La taille d'un tableau ne peut pas être modifiée une fois construite. Vous devez donc passer un nouveau tableau au lieu de modifier le tableau existant.

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

J'avais le même problème.

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

Et puis passé l'obj comme argument de varargs. Ça a marché.

1
Srijit Paul