martes, 17 de febrero de 2009

Anotaciones en Java 5+

Cuando salió Java 5 el código se lleno de anotaciones. Para los que no las ubiquen son esas sentencias que comienzan con una @ y que se colocan antes de la declaración de una clase o de un atributo o de un método:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

@Deprecated
public class AlgunaClaseTest {
    @Before
    public void setUp() {
    }

    @Test
    public void testAlgo() {
    }

    @After
    public void tearDown() {
    }
}

Al principio me disgustaban ya que a mi gusto ensuciaban el código y rompian con las reglas de sintaxis basicas de Java (the simpler the better), pero pasado de un tiempo es posible por fin tenerles agrado después de ver todas sus ventajas.

Una anotación se define asi en el código:

public @interface Anotacion {
}

O asi en caso de que quisieramos parametros:

public @interface AnotacionParametro {
    String parametro();
}
public @interface AnotacionParametros {
    int id();
    String valor();
}

Las anotaciones pasadas se podrian usar de este modo en una clase

@Anotacion
public class AlgunaClase {
    // Cuando es un solo parametro se puede omitir el nombre y el '='
    @AnotacionParametro("parametro")
    public void algunMetodo() {
    }

    @AnotacionParametros(id=1, valor="algo")
    public void algunOtroMetodo() {
    }
}

Es importante al ver el código el no confundir a la a anotación con una interface tradicional, las anotaciones no se heredan ni forman parte del diseño de clases de los sistemas, estas unicamente sirven para agregarle metadatos al miembro sobre el cual se colocan. Se podria decir que son 'comentarios avanzados'.

El ponerle anotaciones al código no cambiaria ABSOLUTAMENTE NADA en el comportamiento del mismo. La pregunta es entonces: Si no cambia nada, ¿Para qué las quiero?

Ejemplo

Supongamos que queremos crear un programa que permita correr clases tipo pruebas unitarias y que queremos que solamente se ejecuten los métodos que esten definidos como pruebas:

public class TestCase {
    @Test
    public void prueba() { }
 
    @Test
    public void excepcion() {
        throw new RuntimeException("Error aqui");
    }
 
    @Test
    public void otraPrueba() { }
 
    @Test
    public void otraExcepcion() {
        throw new RuntimeException("Otro error aqui");
    }
}

Para esto previamente tendríamos que haber definido una anotación llamada Test, la cual deberia de ser importada por todas las clases que se quisieran probar. Nuestra anotación debe de tener definida otra anotación para que funcione del modo que queremos (sí, las anotaciones pueden tener anotaciones :) ), esta es @Retention y tiene como atributo la política de retención RetentionPolicy.RUNTIME para que el compilador la mantenga ahi después de haber creado el archivo class, ya que por default son eliminadas al compilar el archivo. Otra anotación que es conveniente indicar es @Target(ElementType.METHOD) para evitar que sea colocada en otro elemento que no sea un método. Estas anotaciones y enumeraciones estan todas en el paquete java.lang.annotation

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

Y ahora si, nuestro Test Runner quedaria del siguiente modo:

import java.lang.reflect.*;

public class TestRunner {
    public static void main(String[] args) throws Exception {
        int exito = 0;
        int fallas = 0;
        
        Class clazz = Class.forName(args[0]);
        Object obj = clazz.newInstance();
        
        for (Method m : clazz.getMethods()) {
            if (m.isAnnotationPresent(Test.class)) {
                try {
                    m.invoke(obj);
                    System.out.printf("Prueba %s ejecutado%n", m.getName());
                    exito++;
                } catch (Throwable t) {
                    System.out.printf("Prueba %s fallo: %s %n", m, t.getCause());
                    fallas++;
                }
            }
        }
        System.out.printf("OK: %d, Fallas: %d%n", exito, fallas);
    }
}

El código es muy sencillo, unicamente crea un objeto usando la clase pasada como primer argumento en la línea de comandos y ejecuta todos los métodos que tengan una anotación del tipo Test.class definida, hace un conteo de errores y finalmente muestra el resumen.

Conclusión

Muchos de los frameworks actuales utilizan reflection muchísimo (Spring, JUnit, Hibernate, JSF, etc, etc, etc...), la implementación de anotaciones desde Java 5 ha permitido que algunos de estos se mantengan alejados del comportamiento del código del sistema y unicamente utilicen los metadatos para llevar a cabo sus tareas, favoreciendo asi la limpieza y legibilidad del código.

Es importante que aunque sea rara la vez que tengamos que programar una nueva anotación, sepamos emplearlas correctamente y sacarles provecho para tener sistemas mas limpios y menos acoplados.

Links

Aqui se puede encontrar la referencia oficial de Sun (in english of course) sobre anotaciones, el ejemplo se tomo de aqui y fue mejorado en este post ;) - http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html

8 comentarios:

  1. Publica tutoriales de Action Script donde manejes leyes físicas!!!; como cae un hombre de un edificio y su velocidad aumenta y que tan lejos salpica la sangre por la fuerza con la que se estrella su cabeza, cosas asi.

    Saludos Chiquita.

    -A

    ResponderEliminar
  2. Chale, blog ñoño con comentarios de ñoños que no entienden lo que leen (lo digo por el alex no por mi)

    ResponderEliminar
  3. Gracias por el post. Es claro y explica bien los conceptos.

    ResponderEliminar
  4. Excelente publicación, para nosotros los que no teníamos ni idea de como codificar anotaciones en Java y que a partir de este momento ya logramos este código, así que gracias por compartir el conocimiento. Atte: Alfonso.

    ResponderEliminar
  5. así que esas eran las recientemente populares anotaciones! n_n ... gracias por compartir tu conocimiento ;)

    ResponderEliminar
  6. Para Alex: ve a aprender html creo que se te hará mas fácil, deja a JAVA para personas con un poquito mas de IQ.

    PD: Excelente explicacion, llevo 6 paginas webs y esta es la unica q me lo ha explicado bien xD

    ResponderEliminar