Varargs en Java

1. Introducción

Los varargs se introdujeron en Java 5 y proporcionan una forma abreviada de métodos que admiten una cantidad arbitraria de parámetros de un tipo.

En este artículo, veremos cómo podemos usar esta característica principal de Java.

2. Antes de Varargs

Antes de Java 5, siempre que queríamos pasar un número arbitrario de argumentos, teníamos que pasar todos los argumentos en una matriz o implementar N métodos (uno para cada parámetro adicional):

public String format() { ... } public String format(String value) { ... } public String format(String val1, String val2) { ... }

3. Uso de Varargs

Varargs nos ayuda a evitar escribir código repetitivo al introducir la nueva sintaxis que puede manejar una cantidad arbitraria de parámetros automáticamente, usando una matriz debajo del capó.

Podemos definirlos usando una declaración de tipo estándar, seguida de una elipsis:

public String formatWithVarArgs(String... values) { // ... }

Y ahora, podemos llamar a nuestro método con un número arbitrario de argumentos, como:

formatWithVarArgs(); formatWithVarArgs("a", "b", "c", "d");

Como se mencionó anteriormente, los varargs son arreglos, por lo que debemos trabajar con ellos como lo haríamos con un arreglo normal.

4. Reglas

Los varargs son fáciles de usar. Pero hay algunas reglas que debemos tener en cuenta:

  • Cada método solo puede tener un parámetro varargs
  • El argumento varargs debe ser el último parámetro

5. Contaminación de la pila

El uso de varargs puede provocar la denominada contaminación del montón. Para comprender mejor la contaminación de la pila, considere este método de varargs :

static String firstOfFirst(List... strings) { List ints = Collections.singletonList(42); Object[] objects = strings; objects[0] = ints; // Heap pollution return strings[0].get(0); // ClassCastException }

Si llamamos a este extraño método en una prueba:

String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList()); assertEquals("one", one);

Obtendríamos una ClassCastException aunque ni siquiera usamos ningún tipo explícito de conversión aquí:

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

5.1. Uso seguro

Cada vez que usamos varargs , el compilador de Java crea una matriz para contener los parámetros dados. En este caso, el compilador crea una matriz con componentes de tipo genérico para contener los argumentos.

Cuando usamos varargs con tipos genéricos, ya que existe un riesgo potencial de una excepción fatal en tiempo de ejecución, el compilador de Java nos advierte sobre un posible uso inseguro de varargs :

warning: [varargs] Possible heap pollution from parameterized vararg type T

El uso de varargs es seguro si y solo si:

  • No almacenamos nada en la matriz creada implícitamente. En este ejemplo, almacenamos una lista en esa matriz
  • No permitimos que una referencia a la matriz generada escape del método (más sobre esto más adelante)

Si estamos seguros de que el método en sí usa de forma segura los varargs, podemos usar @SafeVarargs para suprimir la advertencia.

En pocas palabras, el uso de varargs es seguro si los usamos para transferir un número variable de argumentos del llamador al método y nada más.

5.2. Escapar de referencia de Varargs

Consideremos otro uso inseguro de varargs :

static  T[] toArray(T... arguments) { return arguments; }

Al principio, puede parecer que el método toArray es completamente inofensivo. Sin embargo, debido a que deja que la matriz varargs escape a la persona que llama, viola la segunda regla de varargs seguros .

Para ver cómo este método puede ser peligroso, usémoslo en otro método:

static  T[] returnAsIs(T a, T b) { return toArray(a, b); }

Entonces, si llamamos a este método:

String[] args = returnAsIs("One", "Two");

De nuevo, obtendríamos una ClassCastException. Esto es lo que sucede cuando llamamos al método returnAsIs :

  • Para pasar una y b a la toArray método, Java tiene que crear una matriz
  • Dado que el objeto [] puede contener elementos de cualquier tipo, el compilador crea uno
  • El método toArray devuelve el objeto [] dado a la persona que llama
  • Dado que el sitio de la llamada espera un String [], el compilador intenta convertir el Object [] al String [] esperado , de ahí la ClassCastException

Para una discusión más detallada sobre la contaminación de pilas, se recomienda leer el artículo 32 de Effective Java por Joshua Bloch.

6. Conclusión

Varargs puede hacer desaparecer una gran cantidad de texto estándar en Java.

Y, gracias a su autoboxing implícito hacia y desde Array, juegan un papel en la preparación de nuestro código para el futuro.

Como siempre, todos los ejemplos de código de este artículo pueden estar disponibles en nuestro repositorio de GitHub.