El problema: número variable de argumentos
Antes de Java 5, si querías escribir un método que aceptase un número variable de argumentos tenías que sobrecargar el método muchas veces o recibir un array:
// Antes de varargs: muchas sobrecargas
public static int max(int a, int b) { ... }
public static int max(int a, int b, int c) { ... }
public static int max(int a, int b, int c, int d) { ... }
// O recibiendo array (incómodo de llamar)
public static int max(int[] numeros) { ... }
// Uso: max(new int[]{1, 2, 3});
Desde Java 5, los varargs resuelven esto con una sintaxis elegante.
Sintaxis
Un parámetro varargs se declara con tipo... seguido del nombre. Debe ser el último parámetro del método:
public static int max(int... numeros) {
int mayor = Integer.MIN_VALUE;
for (int n : numeros) {
if (n > mayor) mayor = n;
}
return mayor;
}
Y se llama con cualquier número de argumentos:
max(1, 2, 3);
max(10, 20, 30, 40, 50);
max(); // sin argumentos: numeros es un array vacío
Internamente es un array
Dentro del método, el parámetro varargs es un array normal del tipo declarado:
public static void info(String... palabras) {
System.out.println("Recibidas " + palabras.length + " palabras:");
for (int i = 0; i < palabras.length; i++) {
System.out.println(i + ": " + palabras[i]);
}
}
Puedes iterarlo con for-each, usar índices y preguntar por length. Es exactamente un String[].
Combinar con parámetros obligatorios
Los parámetros obligatorios van antes del varargs:
public static String unir(String separador, Object... valores) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < valores.length; i++) {
if (i > 0) sb.append(separador);
sb.append(valores[i]);
}
return sb.toString();
}
// Uso
unir(", ", "Ana", "Bob", "Carmen"); // "Ana, Bob, Carmen"
unir(" | ", 1, 2, 3); // "1 | 2 | 3"
Solo puede haber un varargs por método y debe ser el último parámetro. Esto es ilegal:
// ERROR: varargs no puede ir antes de otro parámetro
public static void x(int... nums, String nombre) { }
Pasar un array a un varargs
Si ya tienes un array, puedes pasarlo directamente:
int[] datos = {5, 2, 8, 1, 9};
int mayor = max(datos); // válido
Para pasar una colección, convierte primero:
List<String> nombres = List.of("Ana", "Bob");
String resultado = unir(", ", nombres.toArray());
Ejemplos en la API estándar
El JDK usa varargs constantemente:
// String.format
String.format("Nombre: %s, edad: %d", "Ana", 30);
// List.of, Set.of, Map.of (Java 9+)
List<Integer> lista = List.of(1, 2, 3, 4, 5);
Set<String> set = Set.of("a", "b", "c");
// Arrays.asList
List<Integer> otros = Arrays.asList(10, 20, 30);
// Collections.addAll
Collections.addAll(lista, 6, 7, 8);
// Stream.of
Stream<String> s = Stream.of("x", "y", "z");
// Path.of (NIO)
Path p = Path.of("home", "user", "docs");
Sobrecarga y ambigüedades
La sobrecarga con varargs puede generar resolución ambigua. Java prefiere siempre la versión más específica:
public static void log(String mensaje) { System.out.println("string: " + mensaje); }
public static void log(String... mensajes) { System.out.println(mensajes.length + " strings"); }
log("uno"); // llama a log(String): más específica
log("uno", "dos"); // llama a log(String...)
log(); // llama a log(String...) con array vacío
Cuando haya duda, el compilador elige la no-varargs si cuadra; si no, la varargs.
Varargs de tipos genéricos: advertencia @SafeVarargs
Los varargs de tipos genéricos (List<String>...) generan un warning porque el array interno no preserva el tipo genérico (type erasure). Si tu método es seguro, añade @SafeVarargs:
@SafeVarargs
public static <T> List<T> unirListas(List<T>... listas) {
List<T> resultado = new ArrayList<>();
for (List<T> l : listas) {
resultado.addAll(l);
}
return resultado;
}
@SafeVarargs solo puede aplicarse a métodos static, final o constructores (donde no puede haber polimorfismo que rompa la seguridad).
Buenas prácticas
- Usa varargs cuando el número de argumentos típico sea pequeño y variable (0-5). Para listas de tamaño arbitrario, prefiere recibir
List<T>oCollection<T>. - No abuses de varargs para aceptar "cualquier cosa" con
Object...: pierdes seguridad de tipos. - Cuidado al sobrecargar métodos con varargs; puede haber conflictos con autoboxing o covariance.
- Declara colecciones inmutables con
List.of(...)/Set.of(...)/Map.of(...): usan varargs por debajo.
Los varargs son una herramienta de conveniencia para APIs limpias; bien usados mejoran legibilidad, mal usados esconden costes de creación de arrays.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Java es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Java
Explora más contenido relacionado con Java y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Declarar métodos con la sintaxis tipo... nombre. Comprender que varargs internamente es un array. Combinar parámetros obligatorios con varargs. Llamar a varargs con argumentos, array o colección (toArray()). Evitar ambigüedades al sobrecargar métodos con varargs. Conocer ejemplos reales: String.format, List.of, Arrays.asList.