martes, 31 de marzo de 2009

Annotations

Más allá del @SuppressWarnings("unchecked"), @Override, ect... no había usado las anotaciones hasta que las usamos con Hibernate, pero no había pasado de ahí.

Hace poco, intenté aprovechar su potencial, creando mis propias anotaciones, lo que resulto ser muy sencillo. Supongamos que queremos implementar un método genérico que realice comprobaciones sobre algunos campos de la instancia pasada por parámetro. Lo primero que debemos hacer es poder marcar en nuestras clases qué campos deben ser comprobados, y qué comprobación se debe realizar. Para ello, usaré mi propia anotación en los getters de los mismos:


package es.gmr.pruebas;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MiAnotacion {
   int opcion ();
}


Del código anterior cabe destacar:
  1. @Retention: con esta anotación especificamos el tiempo de vida que va a tener
    nuestra nueva anotación: RetentionPolicy.SOURCE indica que la
    anotación sólo debe ser visible para el código fuente e ignorada por el compilador y
    la máquina virtual, RetentionPolicy.CLASS es tenida en cuenta por el compilador
    pero ignorada por la máquina virtual, y RetentionPolicy.RUNTIME va a ser tenida
    en cuenta por la máquina virtual en tiempo de ejecución.
  2. @Target: cuando desarrollamos una nueva anotación, deberemos especificarle a
    qué tipo de elemento del código es aplicable. De esta manera, podemos acceder a
    los tipos por medio de constantes definidas en la clase ElementType. En este caso se ha indicado que serán aplicadas a los métodos, ya que la usaremos en los getters.
El siguiente paso es añadir esta anotación en donde nos interese:

package es.gmr.pruebas;
public class MiClase {
   public String campo1;
   public String campo2;
   public String campo3;

   @MiAnotacion(opcion = 1)
   public String getCampo1() {
      return campo1;
   }
   public void setCampo1(String campo1) {
      this.campo1 = campo1;
   }

   @MiAnotacion(opcion = 2)
   public String getCampo2() {
      return campo2;
   }
   public void setCampo02(String campo2) {
      this.campo2 = campo2;
   }

   public String getCampo3() {
      return campo3;
   }
   public void setCampo3(String campo3) {
      this.campo3 = campo3;
   }
}

En MiClase hemos indicado que al campo1 se le aplicará la comprobación marcada con un 1 (lo ideal sería usar constantes para cada tipo de comprobación), al campo2 la comprobación marcada con un 2 y sobre el campo3 no se realizarán comprobaciones.

Por último implementamos el método que realice las comprobaciones:


package es.gmr.pruebas;
import java.lang.reflect.*;
public class Comprobacion {
   public void comprobar(Object obj){
      Method[] methods =          command.getClass().getMethods();
      for (Method metodo : methods){
         if (!metodo.getName().startsWith("get")){
            continue;
         }

         MiAnotacion aux =
            metodo.getAnnotation(MiAnotacion .class);
         if (aux!= null){
            switch (aux.opcion()){
               ...
            }
         }
   }
}

domingo, 22 de marzo de 2009

Plugin: EclEmma

Siempre que implemento un test, para un servicio por ejemplo, me surgen las mismas preguntas ¿se me habrá quedado partes del código sin verificar? ¿qué otras pruebas merece la pena añadir? ¿...? Con estas preguntas en mente, estuve buscando en internet y encontré lo que para mí, es simplemente una maravilla: EclEmma. Nominada a mejor herramienta de desarrollo Open Source basada en Eclipse del 2007, la verdad es que ofrece razones para ello.

EclEmma es un plugin Java de cobertura de código para Eclipse. Internamente está basado en la herramienta de cobertura de código Java EMMA. Lo mejor, es que no requiere ningún esfuerzo por parte del desarrollador, basta con instalarla [instrucciones] y reiniciar.

Para ver su funcionamiento, usaremos el siguiente test:


package es.gmr.pruebas;
import junit.framework.TestCase;
public class Test01 extends TestCase {
public void testCompleto(){
MiClase miClase = new MiClase();
int
valor;
valor = miClase.miMetodo(5, 6);
System.out.println("Valor: " + valor);
valor = miClase.miMetodo(6, 5);
System.out.println("Valor: " + valor);
}
}



package es.gmr.pruebas;
public class MiClase {
public int miMetodo(int x, int y){
if (x > y){
return x;
}
if (y > x){
return y;
}
return 0;
}
}


Luego, pulsando con el botón derecho sobre el test que queremos comprobar, tendremos una nueva opción Coverage as, donde seleccionaremos JUnit Test.


Cuando se haya ejecutado el test, en la vista Coverage, podremos ver qué porcentage del código se ha usado en cada clase. En este caso, vemos que para la clase MiClase sólo se ha usado el 86,7%.


Si visualizamos el código de MiClase, vemos que el plugin ha marcado en verde las líneas que se han usado durante el test, y en rojo las que no. Con esto, resulta mucho más sencillo determinar que pruebas faltan por hacer (obligar a que X e Y sean iguales).

Optimizar el uso de String (I)

Puede que una de las caracteriasticas más importantes sobre los String en Java, en cuanto a la optimización, es que son inmutables, es decir, nunca cambian tras su creación. Por ejemplo, si tenemos el siguiente código:

String texto = "Hola";
texto = texto + " y adios";

Al ejecutarse la segunda línea, habrán dos instancias de String en memoria: "Hola" y "Hola y adios". Si estamos implementando un método que realiza varias operaciones como esta, este hecho puede hacer que sea bastante ineficiente. Para solucionarlo, podríamos usar la clase StringBuffer. Por ejemplo, la diferencia en tiempo entre ejecutar este código:

String texto1 = "";
for (int i = 1; i <= 10000; i++)
   texto1 = texto1 + "*";

y este otro:

StringBuffer sb = new StringBuffer();
for (int i = 1; i <= 10000; i++)
   sb.append("*");
String texto2 = sb.toString();

es de 40 a 1, por lo que podemos comprobar que StringBuffer puede resultar una opción interesante cuando vamos a realizar tareas de este tipo.

sábado, 21 de marzo de 2009

Templates

Los Templates son los que se encargan, por ejemplo, de que cuando escribimos for y pulsamos Ctrl + espacio, aparezca un popup con una serie de opciones, y al elegir una de ellas se completa de forma automática el código.

Para gestionarlos, debemos acceder a Window -> Preferences -> Java -> Editor -> Templates.

Supongamos que con frecuecia declaramos variables y las inicialiamos de la forma:

UnaClase miVariable = new UnaClase();

Podríamos crear un template que nos ayude a hacerlo de una formá más rápida. Para ello, pulsamos New en el panel de los templates

En la venta que se nos muestra, le daremos un nombre al template (será lo que se deba escribir para usarlo posteriormente), una descripción y definimos el template. En nuestro caso, el template tendrá la forma:

${tipo} ${nombre} = new ${tipo}();


Por último, le damos a OK y salimos de la gestión de los templates. Ahora, si cuando estemos escribiendo el código escribimos nuevaV y pulsamos Ctrl + Espacio, nos aparecerá el nuevo template:

Breakpoint condicional

Supongamos que queremos depurar un método que está lanzando un NullPointerException. Concretamente, lo lanza en un bucle que itera una lista de mil elementos, y no tenemos ni idea de cual de ellos provoca la excepción. Ir iteración a iteración hasta que el error ocurra puede resultar bastante tedioso. Para evitar esto, Eclipse nos permite definir un breakpoint de forma condicional. De esta forma, podremos decirle que el breakpoint sólo se active cuando la variable sea null, por ejemplo.

Para ello, pulsando con el botón derecho sobre el breakpoint y seleccionamos la opción BreakPoint Properties... El el panel que se muestra, debemos activar la casilla Enable Condition y en el area de texto escribir la condición que queramos (con la ventaja de que podemos autocompletar el texto) haciendo uso de las variables disponibles en este punto del método.

Según el ejemplo mostrado en la imagen anterior, el breakpoint que hemos puesto sólo se activrá cuando la variable usuario sea null.

Inicializar listas

En ocasiones, necesitamos inicializar listas con algunos valores. La forma básica de hacerlo es:

List<String> miLista = new ArrayList<String>();
miLista.add("1");
miLista.add("2");

Una alternativa, sería hacerlo de esta otra forma:


List<String> miLista =
new ArrayList<String>() {{ add("1"); add("2"); }};

pero no cumple las recomenadciones de estilo, con lo quedaría bastante engorroso también:

List<String> miLista = new ArrayList<String>() {
{
add("1");
add("2");
}
};

La segunda alternativa, que nos deja un código más límpio, sería:

List<String> miLista = Arrays.asList(new String[]{"1", "2"});

Que sería la mejor solución si se usa Java 1.4. Pero si usamos Java 1.5, la mejor opción sería:

List<String> miLista = Arrays.asList("1", "2");