java annotations retention

¿Cuál es la razón de java.lang.annotation.Retention?



annotations (4)

Soy muy consciente del significado de la RetentionPolicy de RetentionPolicy y sé lo que hacen y cuándo parece tener sentido usarlos . Para mi propia anotación, sé exactamente si se necesitan en tiempo de ejecución, en archivos de clase o solo para la compilación. Sin embargo, con cualquier anotación definida en una biblioteca, IMHO nunca estará seguro.

Por ejemplo, javax.annotation.Generated está destinado a marcar el código generado, pero rara vez es útil. Como AFAIK tiene más herramientas trabajando en el bytecode que las herramientas que trabajan con la fuente, la información desaparece justo antes de que pueda usarse.

Como las anotaciones ausentes en el tiempo de ejecución no lanzan la ClassNotFoundException (a diferencia de, por ejemplo, las interfaces faltantes), el uso de RetentionPolicy.RUNTIME parece no causar daño. ¿O me equivoco?

¿O fue el ahorro de unos pocos bytes la razón para usar diferentes Retention ? A mí me parece que muchos problemas merecen la pena. ¿Qué me estoy perdiendo?


Por ejemplo, javax.annotation.Generated está destinado a marcar el código generado, pero rara vez es útil. Como AFAIK tiene más herramientas trabajando en el bytecode que las herramientas que trabajan con la fuente, la información desaparece justo antes de que pueda usarse.

Eche un vistazo al editor de código fuente, Android Studio derivado de JetBrains y muchos otros IDE, necesitan trabajar en el código fuente y brindan una excelente experiencia de edición solo por las anotaciones de tiempo de compilación.

Mientras se edita la clase (aún no se ha compilado), el editor puede almacenar y procesar anotaciones.

Ejemplo:

@SuppressWarnings permite suprimir las advertencias, ¿cómo podría hacerlo? C # le permite definir #PRAGMA , #IF , algún tipo de compilación condicional. Ninguna información de compilación condicional se almacena en la salida compilada.

@Override permite que el compilador de Java verifique si la clase base tiene un método para anular o no, si define un nuevo método con parámetros erróneos, el compilador java compilará la clase con un nuevo método con sobrecarga, pero en presencia del compilador java de @Override dará Usted es un error que la firma no coincide correctamente para anular el método.

@GeneratedCode permite que IDE omita clases y miembros que se muestran cuando busca con "Buscar y reemplazar", y le permite operar el IDE solo en su código y no en el generado. ¿Has visto R.* para recursos en Android, estas clases generadas están ocultas en Android Studio pero proporcionan listas de finalización de código útiles?

De manera similar, muchas de estas anotaciones le permiten hacer análisis de código, escribir pruebas unitarias, etc. y trabajar más con él antes de compilar.

Aqui hay mas

Muchos marcos ORM utilizan anotaciones de tiempo de compilación y generan clases adicionales útiles que se utilizan para consultas escritas y otras clases auxiliares para crear tablas y mantener el esquema.

Conclusión

En el ejemplo que se muestra arriba, está claro que las tres y muchas de esas anotaciones innecesariamente agregarán tantos bytes que son totalmente inútiles en tiempo de ejecución.

Java tenía dos opciones, una era agregar algún tipo de anotación de tiempo de compilación usando las #IF etc. usadas en lenguaje basado en c. Lo que requeriría, nueva sintaxis y nueva experiencia de edición, etc., y otra era crear Retention . Fue un buen movimiento crear Retention sin romper la sintaxis.


@Retencion

Esta anotación especifica cómo se almacena la anotación marcada en el tiempo de ejecución de Java. Ya sea que se limite solo al código fuente, incrustado en el archivo de clase generado o también estará disponible en tiempo de ejecución a través de la reflexión. por lo tanto, indica cuánto tiempo se conservarán las anotaciones con el tipo anotado. Si no hay una anotación de retención en una declaración de tipo de anotación, la política de retención se establece de manera predeterminada en RetentionPolicy.CLASS.

Una política de retención determina en qué punto se debe descartar la anotación. Java definió 3 tipos de políticas de retención a través de la enumeración java.lang.annotation.RetentionPolicy. Tiene FUENTE, CLASE y DURACIÓN. La anotación con la política de retención FUENTE se retendrá solo con el código fuente y se descartará durante el tiempo de compilación. Las anotaciones con la política de retención CLASS se conservarán hasta la compilación del código y se descartarán durante el tiempo de ejecución. La anotación con la política de retención RUNTIME estará disponible para la JVM a través del tiempo de ejecución. La política de retención se especificará mediante el uso de la anotación incorporada java @Retention, y tenemos que pasar el tipo de política de retención.

El tipo de política de retención predeterminado es CLASS.


El propósito principal de las anotaciones es llevar metadatos para una unidad de compilación. La mayoría de las anotaciones estándar expresan claramente metainformación que ayuda con el desarrollo y la compilación del código (especificando las propiedades que son válidas por IDE o compilador).

Las anotaciones no están diseñadas para modificar la semántica del lenguaje en tiempo de ejecución. Por lo tanto, si una anotación está disponible o no en tiempo de ejecución no cambia la ejecución. (Por supuesto, si utiliza activamente la metainformación para ajustar su comportamiento de implementación, entonces todo es posible).

Si dentro de un contenedor de biblioteca, en algún lugar se marca una anotación como RetentionPolicy.RUNTIME , se espera claramente que acceder a la anotación desde el tiempo de ejecución (mediante la reflexión) sea útil para los usuarios posteriores.
Si al mismo tiempo la implementación de la anotación es de otra biblioteca, entonces tal expectativa no está garantizada o se debe a un propósito específico de esta anotación que solo puede ser útil para ciertos casos de uso. (Y la construcción de diferentes versiones de jar solo para diferentes configuraciones de retención no es apropiada).

Por lo tanto, si un desarrollador está marcando una anotación como RetentionPolicy.RUNTIME hay un claro caso de uso en mente donde se espera el acceso en tiempo de ejecución. Si a la implementación de la anotación se le proporciona el mismo jar o uno diferente puede ser independiente del caso de uso (por ejemplo, basado en otros criterios de estructuración). En cualquier caso, si tiene la intención de beneficiarse de este caso de uso, tendrá esta biblioteca de anotaciones dentro de su ruta de clase (ya que también podría necesitar otros componentes) y todo está bien. Si no se aplica a este caso de uso, no se vería afectado por la falta de implementación de la anotación.

Reescritura a lo largo de su pregunta:

el uso de la retención RUNTIME no hace ningún daño al programa, además de los ejecutables de saturación (bytecode) con información muerta. El uso de la retención RUNTIME solo donde se espera el uso en tiempo de ejecución de la metainformación (y se considera útil) se agrega a la calidad del código (en términos de capacidad de mantenimiento y comprensión).


La inspiración para las anotaciones de Java ocurrió antes de 2002, en torno a la transición de Java 1.3 a Java 1.4. Un escritorio de alta especificación en esos días era un Pentium 4 a aproximadamente 2,5 GHz o un Athlon XP + a aproximadamente 2 GHz, la RAM sería de 256 o 512 MB. Por ejemplo, una revisión here .

El problema era cómo almacenar y recuperar metadatos sobre el código. La solución típica fue mediante el uso de archivos XML que no fueron verificados por tipo o directamente vinculados al código fuente. Otros ya estaban extendiendo informalmente JavaDoc (el código fuente y la API de extensión estaban presentes en el JDK) para las herramientas de generación de código. La solución, Annotations, es hack (un muy buen hack) que extendió Javadoc y la especificación de clase JLS.

Está claro que los autores originales estaban preocupados por el rendimiento (y en 2002, Java seguía siendo relativamente lento, la reflexión era muy lenta y el tiempo de ejecución de Java era un enorme problema de memoria; algunas cosas nunca cambian). Esto es de la introducción a JSR-175 :

Debido a que muchas anotaciones solo serán utilizadas por herramientas de desarrollo como los generadores de código auxiliar, tiene poco sentido retener todas las anotaciones en tiempo de ejecución; hacerlo podría aumentar el tiempo de ejecución de la memoria y dañar el rendimiento. Sin embargo, hay algunos tipos de anotación que son útiles en el tiempo de ejecución y algunos que son útiles en herramientas que solo tienen acceso a archivos de clase (no a archivos de origen). Por lo tanto, el compilador almacena ciertas anotaciones en los atributos del archivo de clase (JVMS 4.7), y algunas de estas anotaciones se ponen a disposición para su inspección en tiempo de ejecución a través de nuevas API de reflexión.

Su solución al problema fue dividir el problema en tres casos de uso:

VI. Leer anotaciones

Los consumidores de anotaciones se pueden dividir en tres grupos:

a. "Introspectores": programas que consultan anotaciones visibles en tiempo de ejecución de sus propios elementos de programa. Estos programas cargarán clases anotadas e interfaces de anotación en la máquina virtual. (Por runtime-visible, nos referimos a las anotaciones cuya política de retención es RUNTIME).

segundo. "Herramientas específicas": programas que consultan tipos conocidos de anotación de programas externos arbitrarios. Los generadores de trozos, por ejemplo, entran en esta categoría. Estos programas leerán las clases anotadas sin cargarlas en la máquina virtual, pero cargarán las interfaces de anotación.

do. "Herramientas generales": programas que consultan anotaciones arbitrarias de programas externos arbitrarios (como compiladores, generadores de documentación y navegadores de clases). Estos programas no cargarán clases anotadas ni interfaces de anotación en la máquina virtual. Se dice que dichos programas operan "a la distancia de un brazo".

Esto permitió que (en ese momento) casos de uso significativos de "herramientas específicas" y "herramientas generales" como se definieron anteriormente para hacer su trabajo sin crear una carga en el tiempo de ejecución; para estas herramientas, las anotaciones pueden ser FUENTE o CLASE. Solo las anotaciones que se necesitaron en el tiempo de ejecución (de lo anterior, está claro que esto se consideró un caso de uso minoritario ) se cargarían y retendrían en la JVM.

Entonces, sí, la política de retención se implementó para ahorrar bytes y la sobrecarga en tiempo de ejecución. Si bien esto parece extraño ahora, 2002 era un mundo diferente y la memoria y el rendimiento eran preocupaciones muy reales. Ahora que tenemos 10x de rendimiento y memoria, puede usar la retención RUNTIME de forma segura sin preocupaciones.