Java – Argumentos variables (VarArgs)

En Java existe la posibilidad de llamar a un método con un numero de argumentos variable, ósea un numero de argumentos indefinido o desconocido. Esta posibilidad del lenguaje se llama abreviada y en ingles: VarArgs, y no ah sido “oficialmente” posible hasta la salida de J2SE 5.0. Veamos el antes y el después.

Antes de J2SE 5.0


Cuando necesitamos de un método que acepte una cantidad indeterminada de valores podemos usar un arreglo u otro tipo de colección, veamos un ejemplo con un array:

...
int sumar(int[] nums) {
    int total = 0;

    for (int i=0 ; i < nums.length ; i++) {
        total += nums[i];
    }

    return total
}
...

Este método suma todos los números del arreglo pasado como argumento y nos retorna el resultado, podemos invocarlo de la siguiente forma:

...
int[] array = { 1, 2, 3, 4, 5 };
sumar(array);
...

Así que como vemos podríamos crear un arreglo con la cantidad de números que quisiéramos y pasárselos a nuestro método sumar(int[] nums) al cual no le importara cuantos argumentos lleguen.

Si no usáramos ni un arreglo o colección para esta tarea, que haríamos entonces?, podríamos crear un método y sobrecargarlo con un numero diferente de parámetros cuantas veces necesitemos:

...
int sumar(int a, int b) {
    return (a + b);
}

int sumar(int a, int b, int c) {
    return (a + b + c);
}

int sumar(int a, int b, int c, int d) {
    return (a + b + c + d);
}

int sumar(int a, int b, int c, int d, int e) {
    return (a + b + c + d + e);
}
...

No es una alternativa flexible, es limitada, ocupa mas espacio y es poco elegante. Sin duda nos quedamos con el arreglo.

Sin embargo el ejemplo visto de argumentos variables no es nada nuevo para nadie en Java, que tal el famoso método main (String[] args) ?, veamos:

class EjemploMain {

    public static void main(String[] args) {
        System.out.println("Lista de argumentos:");

        for (int i = 0 ; i < args.length ; i++) {
            System.out.println(i + "- " + args[i]);
        }
    }
}

Luego de compilar este EjemploMain.java lo ejecutamos de la siguiente forma:

java -classpath [Ruta de clases (donde esta nuestro EjemploMain.class)] EjemploMain Hola a todo el Mundo

Si lo tenemos en un tarro (.jar) lo ejecutamos de la siguiente forma:

java -jar [Ruta del archivo jar] Hola a todo el Mundo

La salida es:

Lista de argumentos:
0- Hola
1- a
2- todo
3- el
4- Mundo

Como podemos observar, cualquier aplicación Java ejecutable puede recibir una cantidad de argumentos variable al ser invocada en su método main (String[] args).

Desde J2SE 5.0


Desde a partir de la versión J2SE 5.0 el lenguaje tiene oficialmente su truco de varargs el cual nos facilita un poco la tarea de pasar argumentos variables a un método, de hecho el SDK de Java hace uso de las varargs pudiéndose observar por ejemplo en el código fuente de java.lang.Class y en muchas otras clases.

Ahora veamos como aplicar la nueva sintaxis en el ejemplo visto anteriormente de sumar (int[] nums):

...
int sumar(int... nums) {
    int total = 0;

    for (int i=0 ; i < nums.length ; i++) {
        total += nums[i];
    }

     return total;
}
...

Hasta aquí el único cambio que apreciamos es como quedan declarados los argumentos de nuestro método con el ““:

...
int sumar(int... nums)
...

Sin embargo esta ves para llamar a nuestro método lo haremos de la siguiente manera:

...
sumar(1, 2, 3, 4, 5);
...

A partir de ahora lo que hacemos es ahorrarnos tener que crear el arreglo nosotros mismos, ya que ah quedado en manos del compilador gracias al ““, la llamada que escribimos como: sumar(1, 2, 3, 4, 5), será convertida automáticamente por el compilador en el equivalente: sumar(new int[] {1, 2, 3, 4, 5}).

También podemos llamar a nuestro void main (String[] args) como void main (String … args), obteniendo los mismos resultados en los dos casos.

– Argumentos variables junto a otros parametros –

Si lo necesitamos podemos mesclar el uso de varargs con otros parámetros, aunque con ciertas limitaciones, ya que podemos declarar varios parámetros simples pero solo podemos declarar un vararg, es decir que solo se acepta un vararg por método, y además este debe ir siempre último en los parámetros del método. Por ejemplo:

...
void tablaMultiplicar(int n, int... nums) {
    System.out.println("Tabla de multiplicar por: " + n);

    for (int i=0 ; i < nums.length ; i++) {
        System.out.println(nums[i] + " x " + n + " = " + (nums[i] * n));
    }
}
...

Si llamamos a este método como tablaMultiplicar(5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), nos imprimiría por pantalla la tabla de multiplicar por 5. La salida es:

Tabla de multiplicar por: 5
0 x 5 = 0
1 x 5 = 5
2 x 5 = 10
3 x 5 = 15
4 x 5 = 20
5 x 5 = 25
6 x 5 = 30
7 x 5 = 35
8 x 5 = 40
9 x 5 = 45
10 x 5 = 50

Formular el método anterior de la siguiente forma no seria sintácticamente legal por lo que no podríamos compilarlo:

...
void tablaMultiplicar(in... nums, int n) {
    ...
}
...

El compilador nos marcaria errores en la línea de void tablaMultiplicar (in… nums, int n) diciendo “)” expected y “;” expected.

– Arreglos de argumentos variables –

Una sintaxis admitida pero de uso poco frecuente es la de declarar un parámetro que recibe un numero variable de arreglos. Véase:

class VarArgsArray {

    public static void main(String[] args) {
        char[] abc = {'a', 'b', 'c'};
        char[] def = {'d', 'e', 'f'};
        char[] ghi = {'g', 'h', 'i'};

        varArgsArray(abc, def, ghi);
    }

    static void varArgsArray(char[]... letras) {
        for (int i = 0; i < letras.length; i++) {
           System.out.println("Arreglo: " + i);
           for (int j = 0; j < letras[i].length; j++) {
               System.out.println(letras[i][j]);
           }
        }
    }
}

La salida es:

Arreglo: 0
a
b
c
Arreglo: 1
d
e
f
Arreglo: 2
g
h
i

Como vemos, al método void varArgsArray (char[]… letras) podemos pasarle una indefinida cantidad de arreglos de caracteres, y para recorrerlos usamos dos bucles for anidados como cuando trabajamos con arreglos multi-dimensionales.

– Problemas de ambiguedad –

Existen algunas situaciones en las cuales el compilador nos dará problemas en el momento de compilar. Veamos:

  • Caso 1:
class VarArgs {

    void print(String... varStrings) {
        //algún código
    }

    void print(String[] arrayStrings) {
        //algún código
    }
}

La salida de compilación es:

javac VarArgs.java

VarArgs.java:9: cannot declare both print(java.lang.String[]) and print(java.lang.String…) in
VarArgs
void print (String[] strings)
^
1 error

Aquí la sobrecarga de los métodos void print(…) no se puede realizar porque tener String… varStrings como parámetro es equivalente a tener String[] arrayStrings.

  • Caso 2:
class VarArgs {

    public static void main(String[] args) {
        print("un string", "otro string");
    }

    static void print(String... varStrings) {
        //algún código
    }

    static void print(String oneString, String... varStrings) {
        //algún código
    }
}

La salida de compilación es:

javac VarArgs.java

VarArgs.java:6: reference to print is ambiguous, both method print(java.lang.String…) in VarA
rgs and method print(java.lang.String,java.lang.String…) in VarArgs match
print(“un string”, “otro string”);
^
1 error

Este es un caso obvio, los dos métodos void print(…) tienen parámetros que esperan una cantidad variable de argumentos del mismo tipo de objeto.

  • Caso 3:
class VarArgs {

    public static void main(String[] args) {
        print(1, 2, 3, 4, 5);
        print(1.0, 2.0, 3.0, 4.0, 5.0);
    }

    static void print(int... ints) {
        //algún código
    }

    static void print(double... doubles) {
        //algún código
    }
}

La salida de compilación es:

javac VarArgsjava

VarArgsjava:6: reference to print is ambiguous, both method print(int…) in VarArgs and
method print(double…) in VarArgs match
print(1, 2, 3, 4, 5);
^
1 error

Aquí tenemos un “bug”, es un problema del compilador y esta reportado (con una prioridad de “Very Low”) en la base de datos de bugs de Sun Mycrosystems como se puede ver mejor explicado en este enlace .

Mientras tanto una forma de solucionar este problema seria utilizar argumentos variables solo en uno de los métodos y utilizar un arreglo en el otro:

class VarArgs {

    public static void main(String[] args) {
        print(new int[] { 1, 2, 3, 4, 5 });
        print(1.0, 2.0, 3.0, 4.0, 5.0);
    }

    static void print (int[] ints) {
        //algún código
    }

    static void print (Double... doubles) {
        //algún código
    }
}

Este código compila sin problemas pero nos deja sin poder aprovechar el uso de argumentos variables.

Una mejor solución seria usar los wrappers correspondientes para int y double (Integer y Double respectivamente):

class VarArgs {

    public static void main(String[] args) {
        print(1, 2, 3, 4, 5);
        print(1.0, 2.0, 3.0, 4.0, 5.0);
    }

    static void print(Integer... ints) {
        //algún código
    }

    static void print(Double... doubles) {
        //algún código
    }
}

Este código compila sin problemas y nos permite seguir aprovechando los argumentos variables.


Ejemplos:
* Java – VarArgs


, , ,

  1. #1 por Diego el marzo 3, 2012 - 12:16 am

    Excelente articulo me ha servido…. de mucho Gracias (y)

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: