¿Por qué no es posible extender anotaciones en Java?

227

No entiendo por qué no hay herencia en las anotaciones de Java, al igual que las clases de Java. Creo que sería muy útil.

Por ejemplo: quiero saber si una anotación dada es un validador. Con la herencia, podría navegar reflexivamente por las superclases para saber si esta anotación se extiende a ValidatorAnnotation. De lo contrario, ¿cómo puedo lograr esto?

Entonces, ¿alguien puede darme una razón para esta decisión de diseño?

sinuhepop
fuente
2
Tenga en cuenta, por cierto, que todas las anotaciones se extienden java.lang.annotation.Annotation, es decir, cualquier anotación lo es instanceof, aunque este hecho no se declara explícitamente.
Tomáš Záluský

Respuestas:

166

Sobre la razón por la que no se diseñó de esa manera, puede encontrar la respuesta en las Preguntas frecuentes de diseño de JSR 175 , donde dice:

¿Por qué no admite el subtipo de anotación (donde un tipo de anotación se extiende a otro)?

Complica el sistema de tipo de anotación y hace que sea mucho más difícil escribir "Herramientas específicas".

...

“Herramientas específicas”: programas que consultan tipos de anotación conocidos 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.

Entonces, sí, supongo, la razón es solo KISS. De todos modos, parece que este problema (junto con muchos otros) se está estudiando como parte de JSR 308 , e incluso puede encontrar un compilador alternativo con esta funcionalidad ya desarrollada por Mathias Ricken .

pedromarcia
fuente
67
Bueno, tal vez soy estúpido, pero creo que es una pena que no pueda extender las anotaciones solo para "mantenerlo simple". Al menos, los diseñadores de Java no pensaban lo mismo sobre la herencia de clase: P
sinuhepop
2
Java 8 M7, no parece admitir anotaciones de subclasificación. Qué pena.
Ceki
2
@assylias JEP 104 no se trata de hacer posible la subclase de anotaciones. JEP 104 se implementó en Java 8, pero aún no es posible subclasificar las anotaciones (hacer que una anotación extienda otra anotación).
Jesper
71

Las anotaciones extensibles agregarían efectivamente la carga de especificar y mantener otro sistema de tipos. Y este sería un sistema de tipos bastante único, por lo que no podría simplemente aplicar un paradigma de tipo OO.

Piense detenidamente en todos los problemas cuando introduzca el polimorfismo y la herencia en una anotación (por ejemplo, ¿qué sucede cuando la subanotación cambia las especificaciones de la metaanotación como la retención?)

¿Y toda esta complejidad añadida para qué caso de uso?

¿Quiere saber si una anotación dada pertenece a una categoría?

Prueba esto:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    String category();
}

@Category(category="validator")
public @interface MyFooBarValidator {

}

Como puede ver, puede agrupar y clasificar fácilmente las anotaciones sin dolor excesivo utilizando las facilidades proporcionadas.

Entonces, KISS es la razón para no introducir un sistema de tipo meta-tipo en el lenguaje Java.

[ps editar]

Utilicé la cadena simplemente para demostración y en vista de una anotación meta abierta. Para su propio proyecto dado, obviamente puede usar una enumeración de tipos de categoría y especificar múltiples categorías ("herencia múltiple") para una anotación dada. Tenga en cuenta que los valores son completamente falsos y solo con fines de demostración:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    AnnotationCategory[] category();
}
public enum AnnotationCategory {
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC
}

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS})
public @interface FooBarAnnotation {

}

alphazero
fuente
No funciona imprimirá falso:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F {} class a{ @F public void S() {} } @Test public void blahTest() throws NoSuchMethodException { Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); }
user1615664
Estamos anotando una anotación. un # S tiene una anotación F. F en sí está anotada por C. Intenta eso.
alphazero
Si, eso es correcto. Pero, ¿hay alguna manera de hacerlo más limpiamente en lugar de leer el proxy de Anotación?
user1615664
12

En cierto sentido, ya lo tiene con Anotaciones: meta Anotaciones. Si anota una anotación con metainformación, en muchos aspectos es equivalente a extender una interfaz adicional. Las anotaciones son interfaces, por lo que el polimorfismo realmente no entra en juego, y dado que son de naturaleza estática, no puede haber un despacho dinámico en tiempo de ejecución.

En el ejemplo de su validador, podría simplemente en la anotación obtener el tipo anotado y ver si tiene una metaanotación del validador.

El único caso de uso en el que podría ver que la herencia ayudaría es si quisieras poder obtener la anotación por supertipo, pero eso agregaría una gran cantidad de complejidad, porque un método o tipo dado puede tener dos de esas anotaciones, lo que significa que una matriz debería ser devuelta en lugar de un solo objeto.

Así que creo que la respuesta final es que los casos de uso son esotéricos y complican los casos de uso más estándar, lo que hace que no valga la pena.

Yishai
fuente
5

Los diseñadores del soporte de anotaciones de Java hicieron una serie de "simplificaciones" en detrimento de la comunidad de Java.

  1. Ningún subtipo de anotaciones hace que muchas anotaciones complejas sean innecesariamente feas. Uno no puede simplemente tener un atributo dentro de una anotación que pueda contener una de tres cosas. Uno debe tener tres atributos separados, lo que confunde a los desarrolladores y requiere la validación del tiempo de ejecución para garantizar que solo se use uno de los tres.

  2. Solo una anotación de un tipo dado por sitio. Esto ha llevado a un patrón de anotación de colección completamente innecesario. @Validation y @Validations, @Image y @Images, etc.

El segundo se está remediando en Java 8, pero es demasiado tarde. Se han escrito muchos marcos basados ​​en lo que era posible en Java 5 y ahora estas verrugas API están aquí para quedarse durante mucho tiempo.

Konstantin Komissarchik
fuente
1
No solo eso, también hace que sea imposible definir propiedades que tengan atributos anidados recursivamente, lo cual es compatible con el esquema XML. Esto hace que las anotaciones sean estrictamente menos potentes que XML
usuario 2833557 del
3

Puede que tarde tres años en responder a esta pregunta, pero me pareció interesante porque me encontré en el mismo lugar. Aquí está mi opinión al respecto. Puede ver las anotaciones como enumeraciones. Proporcionan un tipo de información unidireccional: úsela o piérdala.

Tuve una situación en la que quería simular GET, POST, PUT y DELETE en una aplicación web. Tenía muchas ganas de tener una "super" anotación que se llamaba "HTTP_METHOD". Más tarde me di cuenta de que no importaba. Bueno, tuve que conformarme con usar un campo oculto en el formulario HTML para identificar DELETE y PUT (porque POST y GET estaban disponibles de todos modos).

En el lado del servidor, busqué un parámetro de solicitud oculto con el nombre, "_method". Si el valor era PUT o DELETE, anula el método de solicitud HTTP asociado. Dicho esto, no importaba si necesitaba o no extender una anotación para hacer el trabajo. Todas las anotaciones se veían iguales, pero se trataban de manera diferente en el lado del servidor.

Entonces, en su caso, suelte el picor para extender las anotaciones. Trátelos como 'marcadores'. "Representan" cierta información y no necesariamente "manipulan" cierta información.

mainas
fuente
2

Una cosa que se me ocurre es la posibilidad de tener múltiples anotaciones. Entonces podría agregar un validador y una anotación más específica en el mismo lugar. Pero podría estar equivocado :)

Janusz
fuente
2

Nunca pensé en eso, pero ... parece que tienes razón, no hay ningún problema con el recurso de herencia de anotaciones (al menos no veo el problema con él).

Acerca de su ejemplo con la anotación 'validador' : puede explotar el enfoque de 'metaanotación' . Es decir, aplica metaanotaciones particulares a toda la interfaz de anotaciones.

denis.zhdanov
fuente
Puede que tarde tres años en responder a esta pregunta, pero me pareció interesante porque me encontré en el mismo lugar.
mainas
1

el mismo problema que tengo No puedes. Me "discipliné" a mí mismo para escribir propiedades en anotaciones para respetar algunos estándares, así que afuera, cuando obtienes una anotación, puedes "oler" qué tipo de anotación es por las propiedades que tiene.

ante.sabo
fuente