¿Puedo pasar una matriz como argumentos a un método con argumentos variables en Java?

276

Me gustaría poder crear una función como:

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

El problema aquí es que argsse trata como Object[]en el método myFormat, y por lo tanto es un solo argumento a String.format, mientras que me gustaría cada Objecten el argsque se pasa como un nuevo argumento. Como String.formattambién es un método con argumentos variables, esto debería ser posible.

Si esto no es posible, ¿hay algún método como String.format(String format, Object[] args)? En ese caso pude anteponer extraVara argsusar una nueva matriz y pasarlo a ese método.

usuario362382
fuente
8
No puedo evitar preguntarme por qué esta pregunta es un tipo de pregunta "¿es válida?". ¿No podrías haberlo intentado? No te excedas en preguntar, te harás más daño que bien.
kamasheto
21
Es cierto que esto podría haberse probado fácilmente. Sin embargo, lo bueno de una pregunta como esta es que expone el tema y solicita respuestas interesantes.
akf
2
De hecho, probé el código anterior, con la intención de pasar los argumentos de la matriz como argumentos al método. Sin embargo, no me di cuenta de que debería haber agregado extraVar a args primero. Cuando sabe que los argumentos variables se tratan como una matriz (incluso fuera del método), esto es, por supuesto, bastante lógico.
user362382

Respuestas:

181

El tipo subyacente de un método variadic function(Object... args) es function(Object[] args) . Sun agregó varargs de esta manera para preservar la compatibilidad con versiones anteriores.

Por lo que sólo debe ser capaz de anteponer extraVara argsy llamada String.format(format, args).

jasonmp85
fuente
1
Pasar un argumento de tipo X[]a un método x(X... xs)da la siguiente advertencia en 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

Sí, a T...es solo un azúcar sintáctico para a T[].

Parámetros de formato JLS 8.4.1

El último parámetro formal en una lista es especial; puede ser un parámetro de aridad variable , indicado por una elipsis que sigue al tipo.

Si el último parámetro formal es un parámetro de tipo variable de aridad T, se considera que define un parámetro formal de tipo T[]. El método es entonces un método de aridad variable . De lo contrario, es un método de arity fijo . Las invocaciones de un método de aridad variable pueden contener más expresiones de argumentos reales que parámetros formales. Se evaluarán todas las expresiones de argumentos reales que no corresponden a los parámetros formales que preceden al parámetro de aridad variable y los resultados se almacenan en una matriz que se pasará a la invocación del método.

Aquí hay un ejemplo para ilustrar:

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

Y sí, el mainmétodo anterior es válido, porque de nuevo, String...es justo String[]. Además, debido a que las matrices son covariantes, a String[]es an Object[], por lo que también puede llamar de ezFormat(args)cualquier manera.

Ver también


Varargs gotchas # 1: pasando null

Cómo se resuelven los varargs es bastante complicado, y a veces hace cosas que pueden sorprenderlo.

Considere este ejemplo:

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

Debido a cómo se resuelven los varargs, la última instrucción invoca con objs = null, lo que por supuesto causaría NullPointerExceptioncon objs.length. Si desea dar un nullargumento a un parámetro varargs, puede hacer lo siguiente:

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

Preguntas relacionadas

La siguiente es una muestra de algunas de las preguntas que las personas han formulado al tratar con varargs:


Vararg gotchas # 2: agregar argumentos adicionales

Como descubrió, lo siguiente no "funciona":

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

Debido a la forma en que funcionan los varargs, en ezFormatrealidad obtiene 2 argumentos, el primero es a String[], el segundo es a String. Si está pasando una matriz a varargs, y desea que sus elementos sean reconocidos como argumentos individuales, y también necesita agregar un argumento adicional, entonces no tiene más remedio que crear otra matriz que acomode el elemento adicional.

Aquí hay algunos métodos de ayuda útiles:

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

Ahora puedes hacer lo siguiente:

    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: pasar una serie de primitivas

No "funciona":

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

Varargs solo funciona con tipos de referencia. Autoboxing no se aplica a la matriz de primitivas. Los siguientes trabajos:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
poligenelubricantes
fuente
2
El uso de parámetros variables Object ... dificulta el uso de firmas adicionales, ya que el compilador identifica la ambigüedad entre las firmas donde incluso un argumento primitivo se puede autoboxear en Object.
anguila ghEEz
77
Falta un problema: si su método tiene un último parámetro de tipo Object ... y lo llama pasando una Cadena [] por ejemplo. El compilador no sabe si su matriz es el primer elemento de los varargs o si es el equivalente de todos los varargs. Te lo advertirá.
Snicolas
Pasar un argumento de tipo X[]a un método x(X... xs)da la siguiente advertencia en 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

Está bien pasar una matriz; de hecho, equivale a lo mismo

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

es lo mismo que

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

Es solo azúcar sintáctico: el compilador convierte el primero en el segundo, ya que el método subyacente espera una matriz para el parámetro vararg .

Ver

mdma
fuente
4

jasonmp85 tiene razón al pasar una matriz diferente a String.format. El tamaño de una matriz no se puede cambiar una vez construido, por lo que tendría que pasar una nueva matriz en lugar de modificar la existente.

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

Estaba teniendo el mismo problema.

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

Y luego pasó el obj como argumento varargs. Funcionó.

Srijit Paul
fuente
2
Por favor agregue eso también.
Mathews Sunny