martes, 11 de octubre de 2005

Java 5. ¿Que tiene de bueno y de malo?. Parte V


Varargs
Varargs (Variable Arguments) nos permite especificar que un método puede tomar múltiples argumentos de un mismo tipo, es decir, permite que pasar un número indeterminado de argumentos a un método.
En versiones anteriores si queríamos hacer algo así tení­amos que pasar un array de objetos (Object[]) como argumento de un método, ahora con varargs queda más simple y claro el pasaje de parámetros indeterminado a un método, por ejemplo:
public void doIt(Object... args) {
 ...
}
 
this.doIt("Java", "5", "J2EE", new Integer(5));

Varargs se aplica teniendo en cuenta las siguientes reglas sintácticas y semánticas:

- El tipo de datos debe estar seguido de tres puntos (...)
      Type...
- El argumento variable debe ser el último en la lista de argumentos del método.
- Es interpretado como Type[]


De esta forma vemos que varargs lo que hace es ocultar el proceso de transformar el argumento variable en un Type[], creandolo con los parámetros pasados al método para luego invoca al método propiamente dicho pasándole el Type[].
Veamos otro ejemplo, ahora la forma de declarar el método main utilizando varargs se vería asi­:
public static void main(String... args) {
 ...
}

Una de las principales ventajas de varargs es que nos permite reducir el numero de métodos escritos para resolver una funcionalidad, en lugar de tener 4 o 5 métodos con diferentes listas de argumentos, podemos hacer uno solo con argumentos variables. Sin embargo el problema que surge con esto es que podemos llegar a tener un código que se preocupe mas por como trabajar con los argumentos variables que por la lógica de negocios que este debe resolver, por eso hay que tener cuidado y no abusar de este feature.
Otras de las ventajas es que se obtiene un código mas limpio y flexible.

Iterando sobre variable-length argument list
Ahora como hacer para trabajar con un argumento variable? Es muy simple, este es tratado como un array, por lo tanto se lo trabaja como si se estuviera trabajando un Type[].


public void doIt(String name, int ... codes) {
   StringBuilder sb = new StringBuilder("Name = ")
      .append(name)
      .append(" -> ");
   Formatter formatter = new Formatter(sb);
 
   for( int code : codes ) {
      formatter.format("%d ", code);
   }
 
   System.out.println( sb.toString() );
}

La llamada a este método se puede realizar de diferentes formas:
Test test = new Test();
test.doIt("jh"); // codes = new int[]{}
test.doIt("jh", 1); // codes = new int[]{1}
test.doIt("jh", 1,2,3,4,5,6); // codes = new int[]{1,2,3,4,5,6};

La salida del siguiente código es:
Name = jh -> 
Name = jh -> 1
Name = jh -> 1 2 3 4 5 6

Como se resuelve la sobrecarga y sobre escritura de métodos?
Unos de los temas a tener en cuenta cuando utilizamos varargs es la forma en la cual se resuelven la sobre carga y la sobre escritura de los métodos.

Por ejemplo que sucede si tenemos el siguiente código:


public class VargArgs {
 
   public static void main(String[] args) {
      VargArgs va = new VargArgs();
      va.doIt("a", "b", "c");
      va.doIt(1,2,3,4);
      va.doIt(1,2,"d");
      va.doIt("a", new Integer(10));
 
   }
 
   public void doIt(String... a) {
      System.out.println("String..." + Arrays.toString(a));
   }
 
   // Duplicate method.
   // public void doIt(String[] a) {
   //    System.out.println("[]" + Arrays.toString(a));
   // }
 
   public void doIt(Number... n) {
      System.out.println("Number..." + Arrays.toString(n));
   }
 
   public void doIt(Object... o) {
      System.out.println("Object..." + Arrays.toString(o));
   }
}

Como vemos tenemos varios métodos doIt sobrecargados con diferentes lista de argumentos, nada del otro mundo, pero que pasa si ejecutamos el método main de esta clase? Tendremos una salida como la que se muestra a continuación:


String...[a, b, c]
Number...[1, 2, 3, 4]
Object...[1, 2, d]
Object...[a, 10]

Bueno, creo que queda claro a que método se llama en cada lí­nea, pero ahora que sucede si realizamos algunos cambios en el código, como por ejemplo, declaramos un nuevo método doIt que reciba un String[]. Lo que sucede es que obtendremos un error en tiempo de compilación debido a que estaria el método duplicado. Otro cambio que hará que tengamos un error en tiempo de compilación en las lí­neas va.doIt(1,2,"d"); y va.doIt("a", new Integer(10)); al cambiar el signature del método public void doIt(Object... o) por public void doIt(Object[] o), pero porque? Es simple la declaración public void doIt(Object... o) significa que el método puede recibir una cantidad variable de argumentos mientras que public void doIt(Object[] o) significa que el método espera recibir un array de objetos.

Las reglas explicadas para la resolución de métodos explicadas en Java 5. ¿Que tiene de bueno y de malo?. Parte II son aplicadas a varargs.

En lo que respecta a la sobre escritura de métodos veámoslo con un ejemplo.


public class SubVarArgs extends VargArgs {
 
   public static void main(String[] args) {
      SubVarArgs s = new SubVarArgs();
      s.doIt(1,2,3,4);
      s.doIt(new Number[]{1,2,3});
   }
 
   public void doIt(Number... n) {
      System.out.println(this.getClass().getName() + "[]" + Arrays.toString(n));
   }
}

La salida es:


varargs.SubVarArgs[][1, 2, 3, 4]
varargs.SubVarArgs[][1, 2, 3]

Dado que se sobrescribe el método doIt(Number...) ambas llamadas se realizan sobre el objeto de la subclase. Ahora si cambiamos el signature de doIt(Number...) por doIt(Number[]) tendremos la siguiente salida:
Object[][1, 2, 3, 4]
varargs.SubVarArgs[][1, 2, 3]

Se invoca el método doIt(Object...) de la superclase dado que el compilador no entiende que se ha sobrescrito el método, si en cambio, se invoca doIt(Number[]) cuando llamamos el método pasándole un array de Number.
Otro detalle a tener en cuenta es que si cambiamos el signature doIt(Object...) de la superclase por el signature doIt(Object[]) nos dará un error de compilación debido a que no hay método que se resuelva para s.doIt(1,2,3,4).

Como podemos ver varargs es un interesante features de Tiger, pero deberí­a utilizarse con precaución y moderación y solamente cuando el beneficio sea alto. También deberí­a tenerse cuidado a sobrecargar y sobrescribir varargs methods.

No hay comentarios:

Publicar un comentario