Introduccion Basica a Anotaciones en Java


Las anotaciones de Java, introducidas en Java 5, son un mecanismo que permite a los desarrolladores incrustar metadatos directamente en su código. Las anotaciones, que sirven como etiquetas informativas, mejoran la legibilidad del código y ofrecen un enfoque simplificado para transmitir detalles complementarios. Ya sea aprovechando las anotaciones integradas o creando anotaciones personalizadas, comprender los conceptos básicos de esta característica concisa pero poderosa puede mejorar su experiencia de programación Java. En esta exploración, navegaremos a través de los fundamentos, las aplicaciones y el impacto en el mundo real de las anotaciones de Java, liberando su potencial para un código más limpio y expresivo.

Las anotaciones vienen en varias formas, desde anotaciones integradas como @Override y @Deprecated que transmiten significados específicos al compilador o a las herramientas, hasta anotaciones personalizadas creadas por desarrolladores para anotar su propio código con información específica del dominio.

En este articulo vamos a hacer varios tipos de anotaciones personalizadas.

Estructura de anotaciones.

Las anotaciones tienen esta estructura.

//Tipo de ejecucion.
//Alcance.
public @interface MiAnotacion{
//Cuerpo.
}

Alcance de anotaciones y tipos de ejecución.

Los tipos de ejecución de anotaciones pueden ser definidos como:


@Retention(RetentionPolicy.SOURCE)
public @interface MiAnotacion{
//Cuerpo.
}
// En este caso la anotacion es accesible previo a la compilacion
y no puede ser utilizada en tiempo de ejecucion o compilación.

@Retention(RetentionPolicy.CLASS)
public @interface MiAnotacion{
//Cuerpo.
}
// En este caso la anotacion es accesible solo en tiempo de compilación
y no puede ser utilizada en tiempo de ejecucion.

@Retention(RetentionPolicy.RUNTIME)
public @interface MiAnotacion{
//Cuerpo.
}
// En este caso la anotacion existe en tiempo de ejecucion.


Los alcances de anotaciones pueden ser definidos como:

  1. Alcance de clase.
  2. Alcance de propiedado campo.
  3. Alcance de metodo.
  4. Alcance de constructor.
  5. Otros, por ahora solo cubriremos esos que son los mas usados.

1.Alcance de clase.

@Target(ElementType.TYPE)
// En este caso la anotacion puede ser insertada solo en la clase.

// Definimos una clase en ClaseAnotada.class.
@MiAnotacion
// No hay errores de compilación.
public class ClaseAnotada {

@MiAnotacion
    // Error de compilación.
private String propiedad;

@MiAnotacion
    // Error de compilación.
public void metodo(){

}
}



2.Alcance de propiedad o campo.

@Target(ElementType.FIELD)
// En este caso la anotacion puede ser insertada solo en una propiedad.

// Definimos una clase en ClaseAnotada.class.
@MiAnotacion
// Error de compilación.
public class ClaseAnotada {

@MiAnotacion
    // No hay errores de compilación.
private String propiedad;

@MiAnotacion
    // Error de compilación.
public void metodo(){

}
}




3.Alcance de metodo.

@Target(ElementType.METHOD)
// En este caso la anotacion puede ser insertada solo en un metodo.

// Definimos una clase en ClaseAnotada.class.
@MiAnotacion
// Error de compilación.
public class ClaseAnotada {

@MiAnotacion
    // Error de compilación.
private String propiedad;

@MiAnotacion
    // No hay errores de compilación.
public void metodo(){

}
}




4.Alcance de constructor.

@Target(ElementType.CONSTRUCTOR)
// En este caso la anotacion puede ser insertada solo en un constructor.

// Definimos una clase en ClaseAnotada.class.
@MiAnotacion
// Error de compilación.
public class ClaseAnotada {

@MiAnotacion
    // Error de compilación.
private String propiedad;

@MiAnotacion
    // Error de compilación.
public void metodo(){

}
    
    @MiAnotacion
    // No hay errores de compilación.
   public ClaseAnotada(){
        //Constructor
}
}


Ahora vamos a definir una anotacion de clase y procesarla.


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MiAnotacion {
    //Cuerpo.
}

// Definimos un par de clases en ClaseAnotada.class y ClaseNoAnotada.class.

@MiAnotacion
public class ClaseAnotada {

private String propiedad;

public void metodo(){

}
}


public class ClaseNoAnotada {

private String propiedad;

public void metodo(){

}
}

// Definimos una clase main para ejecutar el codigo en Aplicacion.class

public class Aplicacion {

public static void main(String[] args) {
    validarClaseAnotada(ClaseAnotada.class);
    validarClaseAnotada(ClaseNoAnotada.class);
    }

    private static void validarClaseAnotada(Class clase){
    if(clase.isAnnotationPresent(MiAnotacion.class)){
    System.out.println("Esta clase esta anotada con MiAnotacion");
    }else{
    System.out.println("Esta clase no tiene anotaciones");
    }
    }
}


El resultado es:

Esta clase esta anotada con MiAnotacion Esta clase no tiene anotaciones Process finished with exit code 0


A simple vista no parece tener mucha utilidad pero puede ser utilzada por ejemplo en un metodo en una aplicacion web que requera permisos o autenticacion y un filtro que rechaze peticiones si el usuario no tiene permisos o no esta autenticado al invocar el metodo anotado (el ejemplo practico queda para un futuro post).

Ahora definamos una anotacion de alcance de codigo y con cuerpo. 
Vamos a crear la siguiente anotacion de clase.


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Creditos {
String autor() default "Mi Nombre";
String comentarios() default "Ninguno";
}


Para agregar una propiedad al cuerpo se define:
  1. Tipo: String, int, boolean, etc.
  2. nombre()
  3. (Opcional) valor por default.
Y la utilizaremos en la clase no anotada.


@Creditos(autor = "Algun Nombre", comentarios = "Esta clase se llama
ClaseNoAnotada.")
public class ClaseNoAnotada {
private String propiedad;

public void metodo(){

}
}



Por ultimo definamos otro caso para realizar una validacion. Debe hacerse con un anotation processor pero es un tema entero asi hagamos algo simple y en otro post lo desarrollaremos.

Vamos a hacer una validacion de edad.


// Definimos una anotacion con una edad minima y un mensaje.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidarEdad {
int minimo() default 18;
String mensaje() default "La Persona debe ser mayor a 18";
}


// Definimos una clase Persona.class

public class Persona {
private String nombre;
@ValidarEdad
private int edad;

public void setEdad(Integer edad) { this.edad = edad; }
public void setNombre(String nombre) { this.nombre = nombre; }
public int getEdad() { return edad; }
public String getNombre() { return nombre; }
}


// Definimos una clase ValidadorPersona.class que buscara campos
con anotaciones

public class ValidadorPersona {

public static void validar(final Persona persona)
                                        throws IllegalAccessException {
    //Lista todos los campos con la anotacion ValidarEdad.class.
        
        Collection<Field> edades = Arrays.stream(persona.getClass().
        getDeclaredFields()).filter(field ->
    field.isAnnotationPresent(ValidarEdad.class)).
            collect(Collectors.toList());
    //Lista todos los campos que deben validar la edad.
       for (Field campoEdad : edades) {
campoEdad.setAccessible(true);
int edad = campoEdad.getInt(persona);
ValidarEdad anotacion = campoEdad.
                    getAnnotation(ValidarEdad.class);
 
    // Si la edad es menor al minimo lanza una excepcion con el
    mensaje definido en la anotacion.

            if(edad<anotacion.minimo()){
throw new NumberFormatException(anotacion.mensaje());
}
}
}
}

// Lo corremos en la clase main.

public class Aplicacion {

public static void main(String[] args) throws IllegalAccessException {
Persona persona = new Persona();
persona.setEdad(35);
ValidadorPersona.validar(persona);
        //Este llamado no debe fallar.
System.out.println("Persona validada.");
persona.setEdad(10);
        //Este llamado SI debe fallar.
ValidadorPersona.validar(persona);
System.out.println("Persona validada.");
}
}


El Resultado.

Persona validada. //El primero que es 35 es validado. Exception in thread "main" java.lang.NumberFormatException:
La Persona debe ser mayor a 18 at ValidadorPersona.validar(ValidadorPersona.java:16) at Aplicacion.main(Aplicacion.java:11)
//El segundo que es 10 falla.

 
Se puede expandir el validador y agregar mas anotaciones pero la mejor forma de hacerlo es con un procesador de Anotaciones pero como dije es una introduccion y queda para otro post.

Asi que hasta la proxima.

Comentarios