En el lenguaje de programación de computadoras Java , una anotación es una forma de metadatos sintácticos que se pueden agregar al código fuente de Java . [1] Se pueden anotar clases , métodos , variables , parámetros y paquetes Java . Al igual que las etiquetas Javadoc , las anotaciones Java se pueden leer desde archivos fuente. A diferencia de las etiquetas Javadoc , las anotaciones Java también se pueden incrustar y leer desde archivos de clase Java generados por el compilador de Java . Esto permite que la máquina virtual Java retenga las anotaciones en tiempo de ejecución y las lea mediante reflexión . [2] Es posible crear metanotaciones a partir de las existentes en Java. [3]
La plataforma Java tiene varios mecanismos de anotación ad hoctransient
, por ejemplo, el modificador o la @Deprecated
etiqueta javadoc. La Solicitud de especificación de Java JSR-175 introdujo la función de anotación de propósito general (también conocida como metadatos ) en el Proceso de la comunidad Java en 2002; obtuvo la aprobación en septiembre de 2004. [4]
Las anotaciones estuvieron disponibles en el propio lenguaje a partir de la versión 1.5 del Java Development Kit (JDK). La apt
herramienta proporcionó una interfaz provisional para el procesamiento de anotaciones en tiempo de compilación en JDK versión 1.5; JSR-269 formalizó esto y se integró en el compilador javac en la versión 1.6.
Java define un conjunto de anotaciones integradas en el lenguaje. De las siete anotaciones estándar, tres forman parte de java.lang y las cuatro restantes se importan de java.lang.annotation. [5] [6]
Anotaciones aplicadas al código Java:
@Override
- Comprueba que el método sea una anulación . Provoca un error de compilación si el método no se encuentra en una de las clases principales o en las interfaces implementadas .@Deprecated
- Marca el método como obsoleto. Provoca una advertencia de compilación si se utiliza el método.@SuppressWarnings
- Indica al compilador que suprima las advertencias de tiempo de compilación especificadas en los parámetros de anotación.Anotaciones aplicadas a otras anotaciones (también conocidas como "Meta anotaciones"):
@Retention
- Especifica cómo se almacena la anotación marcada, ya sea solo en código, compilada en la clase o disponible en tiempo de ejecución mediante reflexión.@Documented
- Marca otra anotación para su inclusión en la documentación.@Target
- Marca otra anotación para restringir a qué tipo de elementos Java se puede aplicar la anotación.@Inherited
- Marca otra anotación para ser heredada a las subclases de la clase anotada (por defecto, las anotaciones no son heredadas por las subclases).Desde Java 7, se han agregado tres anotaciones adicionales al lenguaje.
@SafeVarargs
- Suprime las advertencias para todos los llamadores de un método o constructor con un parámetro varargs genérico , desde Java 7.@FunctionalInterface
- Especifica que la declaración de tipo está destinada a ser una interfaz funcional , desde Java 8.@Repeatable
- Especifica que la anotación se puede aplicar más de una vez a la misma declaración, desde Java 8.Este ejemplo demuestra el uso de la @Override
anotación. Le indica al compilador que verifique las clases principales en busca de métodos coincidentes. En este caso, se genera un error porque el gettype()
método de la clase Gato en realidad no anula getType()
la clase Animal como se desea, debido a que no coinciden los casos . Si la anotación estuviera ausente, se crearía @Override
un nuevo método de nombre en la clase Cat.gettype()
clase pública Animal { habla pública vacía () { } public String getType () { return "animal genérico" ; } } public class Cat extends Animal { @Override public void talk () { // Esta es una buena anulación. Sistema . afuera . println ( "Miau." ); } @Override public String gettype () { // Error en tiempo de compilación debido a un error tipográfico: debería ser getType() no gettype(). devolver "gato" ; } }
Las declaraciones de tipo de anotación son similares a las declaraciones de interfaz normales. Un signo de arroba (@) precede a la palabra clave "interfaz".
// @Twizzle es una anotación al método toggle(). @Twizzle alternancia de vacío público () { } // Declara la anotación Twizzle. @interfaz pública Twizzle { }
Las anotaciones pueden incluir un conjunto de pares clave-valor, que se modelan como métodos del tipo de anotación. Cada declaración de método define un elemento del tipo de anotación. Las declaraciones de métodos no deben tener ningún parámetro ni una cláusula de lanzamiento. Los tipos de retorno están restringidos a primitivos , cadenas , clases, enumeraciones , anotaciones y matrices de los tipos anteriores. Los métodos pueden tener valores predeterminados .
// Igual que: @Edible(valor = verdadero) @Edible ( verdadero ) Artículo elemento = nueva Zanahoria (); public @interface comestible { valor booleano () predeterminado falso ; } @Author ( primero = "Oompah" , último = "Loompah" ) Libro libro = nuevo Libro (); public @interface Autor { Cadena primero (); Cadena última (); }
Las propias anotaciones pueden incluir anotaciones para indicar dónde y cuándo se pueden utilizar:
@Retention ( RetentionPolicy . RUNTIME ) // Hacer que esta anotación sea accesible en tiempo de ejecución mediante reflexión. @Target ({ ElementType . METHOD }) // Esta anotación solo se puede aplicar a métodos de clase. @interface pública Tweezable { }
El compilador reserva un conjunto de anotaciones especiales (incluidas @Deprecated
y ) con @Override
fines @SuppressWarnings
sintácticos.
Los marcos suelen utilizar anotaciones como una forma de aplicar convenientemente comportamientos a clases y métodos definidos por el usuario que, de otro modo, deben declararse en una fuente externa (como un archivo de configuración XML) o mediante programación (con llamadas API). La siguiente, por ejemplo, es una clase de datos JPA anotada :
@Entity // Declara esto como un bean de entidad @Table ( nombre = "personas" ) // Asigna el bean a la tabla SQL "personas" clase pública Persona implementa Serializable { @Id // Asigna esto a la columna de clave principal. @GeneratedValue ( estrategia = GenerationType . AUTO ) // La base de datos generará nuevas claves primarias, no nosotros. identificación entera privada ; @Column ( longitud = 32 ) // Truncar los valores de la columna a 32 caracteres. nombre de cadena privada ; público entero getId () { identificación de retorno ; } public void setId ( ID entero ) { this . identificación = identificación ; } cadena pública getName () { nombre de retorno ; } public void setName ( nombre de cadena ) { this . nombre = nombre ; } }
Las anotaciones no son llamadas a métodos y, por sí solas, no harán nada. Más bien, el objeto de clase se pasa a la implementación JPA en tiempo de ejecución , que luego extrae las anotaciones para generar un mapeo objeto-relacional .
A continuación se ofrece un ejemplo completo:
paquete com.annotation ; importar java.lang.annotation.Documented ; importar java.lang.annotation.ElementType ; importar java.lang.annotation.Inherited ; importar java.lang.annotation.Retention ; importar java.lang.annotation.RetentionPolicy ; importar java.lang.annotation.Target ; @Documented @Retention ( RetentionPolicy . RUNTIME ) @Target ({ ElementType . TYPE , ElementType . METHOD , ElementType . CONSTRUCTOR , ElementType . ANNOTATION_TYPE , ElementType . PACKAGE , ElementType . FIELD , ElementType . LOCAL_VARIABLE }) @Inherited public @interface Sin terminar { enum público Prioridad { BAJO , MEDIO , ALTO } Valor de cadena (); Cadena [] cambiada por () predeterminada "" ; Cadena [] lastChangedBy () predeterminado "" ; Prioridad prioridad () Prioridad predeterminada . MEDIO ; Cadena creada por () predeterminada "James Gosling" ; Cadena lastChanged () predeterminada "2011-07-08" ; }
paquete com.annotation ; public @interface UnderConstruction { Propietario de la cadena () predeterminado "Patrick Naughton" ; Valor de cadena () predeterminado "El objeto está en construcción". ; Cadena creada por () predeterminada "Mike Sheridan" ; Cadena lastChanged () predeterminada "2011-07-08" ; }
paquete com.validadores ; importar javax.faces.application.FacesMessage ; importar javax.faces.component.UIComponent ; importar javax.faces.context.FacesContext ; importar javax.faces.validator.Validator ; importar javax.faces.validator.ValidatorException ; importar com.annotation.UnderConstruction ; importar com.annotation.Unfinished ; importar com.annotation.Unfinished.Priority ; importar com.util.Util ; @UnderConstruction ( propietario = "Jon Doe" ) clase pública DateValidator implementa Validador { validación pública vacía ( contexto FacesContext , componente UIComponent , valor del objeto ) lanza ValidatorException { Fecha de cadena = ( Cadena ) valor ; String errorLabel = "Ingrese una fecha válida". ; if ( ! componente . getAttributes (). isEmpty ()) { errorLabel = ( String ) componente . obtenerAtributos (). obtener ( "errordisplayval" ); } if ( ! Util . validarAGivenDate ( fecha )) { @Unfinished ( changedBy = "Steve" , valor = "si agregar mensaje al contexto o no, confirmar" , prioridad = Prioridad . ALTA ) Mensaje FacesMessage = new FacesMessage (); mensaje . setSeverity ( FacesMessage . SEVERITY_ERROR ); mensaje . establecerResumen ( etiquetaerror ); mensaje . setDetail ( etiquetaerror ); lanzar una nueva ValidatorException ( mensaje ); } } }
Cuando se compila el código fuente de Java, las anotaciones se pueden procesar mediante complementos del compilador llamados procesadores de anotaciones. Los procesadores pueden producir mensajes informativos o crear archivos o recursos fuente Java adicionales, que a su vez pueden compilarse y procesarse. Sin embargo, los procesadores de anotaciones no pueden modificar el código anotado en sí. (Las modificaciones del código se pueden implementar utilizando métodos más allá de la especificación del lenguaje Java). El compilador de Java almacena condicionalmente metadatos de anotación en los archivos de clase, si la anotación tiene un RetentionPolicy
of CLASS
o RUNTIME
. Posteriormente, la JVM u otros programas pueden buscar los metadatos para determinar cómo interactuar con los elementos del programa o cambiar su comportamiento.
Además de procesar una anotación utilizando un procesador de anotaciones, un programador de Java puede escribir su propio código que utilice la reflexión para procesar la anotación. Java SE 5 admite una nueva interfaz definida en el java.lang.reflect
paquete. Este paquete contiene la interfaz llamada AnnotatedElement
que implementan las clases de reflexión de Java, incluidas Class
, Constructor
, Field
, Method
y Package
. Las implementaciones de esta interfaz se utilizan para representar un elemento anotado del programa que se ejecuta actualmente en la máquina virtual Java. Esta interfaz permite leer las anotaciones de forma reflexiva.
La AnnotatedElement
interfaz proporciona acceso a anotaciones que tienen RUNTIME
retención. Este acceso lo proporcionan los métodos getAnnotation
, getAnnotations
y isAnnotationPresent
. Debido a que los tipos de anotaciones se compilan y almacenan en archivos de código de bytes al igual que las clases, las anotaciones devueltas por estos métodos se pueden consultar como cualquier objeto Java normal. A continuación se proporciona un ejemplo completo de procesamiento de una anotación:
importar java.lang.annotation.Retention ; importar java.lang.annotation.RetentionPolicy ; // Esta es la anotación que se procesará // El valor predeterminado para Target son todos los elementos Java // Cambiar la política de retención a RUNTIME (el valor predeterminado es CLASS) @Retention ( RetentionPolicy . RUNTIME ) public @interface TypeHeader { // Valor predeterminado especificado para el atributo de desarrollador Desarrollador de cadenas () predeterminado "Desconocido" ; Cadena última modificación (); Cadena [] miembros del equipo (); int significadoDeVida (); }
// Esta es la anotación que se aplica a una clase @TypeHeader ( desarrollador = "Bob Bee" , lastModified = "2013-02-12" , teamMembers = { "Ann" , "Dan" , "Fran" }, significadoDeVida = 42 ) clase pública SetCustomAnnotation { // El contenido de la clase va aquí }
// Este es el código de ejemplo que procesa la anotación import java.lang.annotation.Annotation ; importar java.lang.reflect.AnnotatedElement ; clase pública UseCustomAnnotation { public static void main ( String [ ] args ) { Clase <SetCustomAnnotation> classObject = SetCustomAnnotation . clase ; leerAnotación ( objeto de clase ); } static void readAnnotation ( elemento AnnotatedElement ) { intenta { System . afuera . println ( "Valores del elemento de anotación: \n" ); if ( element . isAnnotationPresent ( TypeHeader . class )) { // getAnnotation devuelve Tipo de anotación Anotación singleAnnotation = elemento . getAnnotation ( TypeHeader . clase ); encabezado TypeHeader = ( TypeHeader ) singleAnnotation ; Sistema . afuera . println ( "Desarrollador: " + encabezado . desarrollador ()); Sistema . afuera . println ( "Última modificación: " + encabezado . última modificación ()); // miembros del equipo devueltos como String [] System . afuera . imprimir ( "Miembros del equipo: " ); para ( miembro de cadena : encabezado . teamMembers ()) Sistema . afuera . imprimir ( miembro + ", " ); Sistema . afuera . imprimir ( "\n" ); Sistema . afuera . println ( "Significado de la vida: " + encabezado . significadoDeLaVida ()); } } catch ( Excepción excepción ) { excepción . imprimirStackTrace (); } } }