Los metadatos representan información descriptiva sobre el propio código fuente. En lugar de almacenar configuraciones en archivos externos como XML o propiedades, las anotaciones integran esta información directamente en las estructuras del programa. Este enfoque elimina la desconexión entre la lógica y su descripción, simplificando el mantenimiento y reduciendo errores.
Las anotaciones se definen mediante interfaces especiales con @interface. Por defecto, no son heredadas por subclases, excepto cuando se decoran con @Inherited. Este comportamiento solo aplica a anotaciones de nivel clase, siendo ignorado en interfaces, campos o métodos.
Su utilidad principle abarca tres áreas:
- Generación de documentación: Etiquetas como
@paramo@returnen Javadoc - Análisis estático: Reemplazo de configuraciones XML en frameworks modernos (ej: Spring Boot), y gestión de inyección de dependencias
- Verificación en compilación: Validaciones como
@Overridepara métodos sobrescritos
Java proporciona anotaciones integradas:
@Override: Confirma sobrescritura de métodos@Deprecated: Marca elementos obsoletos@SuppressWarnings: Suprime advertencias específicas
Las meta-anotaciones controlan su comportamiento:
@Retention: Define vigencia (SOURCE,CLASS,RUNTIME)@Target: Especifica elementos aplicables (métodos, campos, etc.)@Documented: Incluye en documentación generada@Inherited: Permite herencia en subclases
Para crear anotaciones personalizadas:
package es.ejemplo.anotaciones;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EtiquetaPersonalizada {
String descripcion();
int nivel() default 1;
}
Los parámetros se declaran como métodos sin cuerpo, acpetando tipos básicos, String, enums o clases. Los valores por defecto se establecen con default.
Implementemos un validador de contactos mediante reflexión:
package es.ejemplo.anotaciones;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ValidadorContacto {
TipoContacto tipo();
}
public enum TipoContacto {
CORREO, TELEFONO, CELULAR, SITIO_WEB
}
public class PerfilUsuario {
@ValidadorContacto(tipo = TipoContacto.CORREO)
private String correoElectronico;
@ValidadorContacto(tipo = TipoContacto.CELULAR)
private String numeroCelular;
}
public class Validador {
public static void procesar(PerfilUsuario usuario)
throws IllegalAccessException {
for (Field campo : usuario.getClass().getDeclaredFields()) {
if (campo.isAnnotationPresent(ValidadorContacto.class)) {
campo.setAccessible(true);
ValidadorContacto anotacion =
campo.getAnnotation(ValidadorContacto.class);
String valor = (String) campo.get(usuario);
if (!validarFormato(anotacion.tipo(), valor)) {
System.out.println("Error en " +
campo.getName() + ": " + valor);
}
}
}
}
private static boolean validarFormato(
TipoContacto tipo, String valor) {
return switch(tipo) {
case CORREO -> valor.matches(".+@.+\\.[a-z]+");
case CELULAR -> valor.matches("^[1][3-9]\\d{9}$");
default -> true;
};
}
}
public class EjecucionPrueba {
public static void main(String[] args)
throws IllegalAccessException {
PerfilUsuario usuario = new PerfilUsuario();
usuario.correoElectronico = "usuario@dominio";
usuario.numeroCelular = "13800138000";
Validador.procesar(usuario);
}
}
Al ejecutar, se mostrará:
Error en correoElectronico: usuario@dominio
El procesador utiliza reflexión para acceder a campos anotados, validando formatos según el tipo especificado. Las expresiones regulares pueden ajustarse según requerimientos específicos del dominio.
Las anotaciones operan en tres niveles de persistencia:
SOURCE: Solo en código fuenteCLASS: En bytecode pero no en tiempo de ejecuciónRUNTIME: Accesibles mediante reflexión durante la ejecución
Para elementos anotables, ElementType define contextos como FIELD, METHOD o PARAMETER. La combinación de estos meta-datos con procesamiento reflexivo habilita patrones avanzados sin modificar la estructura base del código.