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

276

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 l' argson traite comme Object[]dans la méthode myFormat, et est donc un seul argument String.format, alors que je voudrais chaque Objectà argsêtre passé comme un nouvel argument. Puisqu'il String.formats'agit également d'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à l' argsaide d'un nouveau tableau et le transmettre à cette méthode.

user362382
la source
8
Je ne peux pas m'empêcher de me demander pourquoi cette question est une sorte de question "est-ce valable". Tu ne pouvais pas juste l'essayer? N'en faites pas trop, vous vous ferez plus de mal que de bien.
kamasheto
21
assez vrai, cela aurait pu être facilement testé. cependant, la bonne chose à propos d'une question comme celle-ci est qu'elle expose le sujet et sollicite des réponses intéressantes.
akf
2
J'ai en fait essayé le code ci-dessus, avec l'intention de passer les arguments du tableau comme arguments à la méthode. Cependant, je ne savais pas que j'aurais dû ajouter extraVar aux arguments en premier. Lorsque vous savez que les arguments variables sont traités comme un tableau (même en dehors de la méthode), cela est bien sûr tout à fait logique.
user362382

Réponses:

181

Le type sous-jacent d'une méthode variadique function(Object... args) est function(Object[] args) . Sun a ajouté des varargs de cette manière pour préserver la compatibilité descendante.

Donc , vous devriez juste être en mesure de préfixer extraVarà argset appel String.format(format, args).

jasonmp85
la source
1
Passer un argument de type X[]dans une méthode x(X... xs)donne l'avertissement suivant dans 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
319

Oui, a T...n'est qu'un sucre syntaxique pour a 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 d' arité variable , indiqué par une élipse suivant le type.

Si le dernier paramètre formel est un paramètre d'arité variable de type T, il est considéré comme définissant un paramètre formel de type T[]. La méthode est alors une méthode à arité variable . Sinon, c'est une méthode d' arité fixe . Les invocations d'une méthode d'arité variable peuvent contenir des expressions d'argument plus réelles que des paramètres formels. Toutes les expressions d'argument réelles qui ne correspondent pas aux paramètres formels précédant le paramètre d'arité variable seront évaluées et les résultats stockés dans un tableau qui sera transmis à l'appel de 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 mainméthode ci-dessus est valide, car encore une fois, elle String...est juste String[]. De plus, comme les tableaux sont covariants, a String[]est un Object[], vous pouvez donc également appeler dans les ezFormat(args)deux sens.

Voir également


Varargs gotchas # 1: en passant null

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

Considérez 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 façon dont les varargs sont résolus, la dernière instruction invoque avec objs = null, ce qui bien sûr entraînerait NullPointerExceptionavec objs.length. Si vous souhaitez donner un nullargument à 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 ont traité des varargs:


Vararg gotchas # 2: ajout d'arguments supplémentaires

Comme vous l'avez découvert, les éléments suivants ne "fonctionnent" pas:

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

En raison de la façon dont varargs fonctionne, ezFormatobtient en fait 2 arguments, le premier étant un String[], le second étant un String. Si vous passez un tableau à varargs, et que vous souhaitez que ses éléments soient reconnus comme des arguments individuels, et que vous devez également 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 d'aide utiles:

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

Vous pouvez maintenant effectuer les opérations suivantes:

    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: passage d'un tableau de primitives

Ça ne "marche" pas:

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

Varargs ne fonctionne qu'avec des types de référence. Le contrôle automatique ne s'applique pas au tableau de primitives. Les oeuvres suivantes:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
polygénelubrifiants
la source
2
L'utilisation de paramètres variadic Object ... rend difficile l'utilisation de signatures supplémentaires car le compilateur identifie l'ambiguïté entre les signatures où même un argument primitif peut être placé automatiquement dans Object.
anguille ghEEz
7
Il y a un gotcha manquant: si votre méthode a un dernier paramètre de type Object ... et que vous l'appelez en passant un String [] par exemple. Le compilateur ne sait pas si votre tableau est le premier élément des varargs ou est l'équivalent de tous les varargs. Cela vous avertira.
Snicolas
Passer un argument de type X[]dans une méthode x(X... xs)donne l'avertissement suivant dans 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

C'est correct de passer un tableau - en fait, cela revient à 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 deuxième, car la méthode sous-jacente attend un tableau pour le paramètre vararg .

Voir

mdma
la source
4

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

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

J'avais le même problème.

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

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

Srijit Paul
la source
2
Veuillez ajouter cela aussi.
Mathews Sunny